- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
显而易见的答案是使用 Charset.defaultCharset()
但我们最近发现这可能不是正确的答案。有人告诉我,结果与 java.io 类在多个场合使用的真实默认字符集不同。看起来 Java 保留了 2 组默认字符集。有人对此问题有任何见解吗?
我们能够重现一个失败案例。这是一种用户错误,但它仍然可能暴露所有其他问题的根本原因。这是代码,
public class CharSetTest {
public static void main(String[] args) {
System.out.println("Default Charset=" + Charset.defaultCharset());
System.setProperty("file.encoding", "Latin-1");
System.out.println("file.encoding=" + System.getProperty("file.encoding"));
System.out.println("Default Charset=" + Charset.defaultCharset());
System.out.println("Default Charset in Use=" + getDefaultCharSet());
}
private static String getDefaultCharSet() {
OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
String enc = writer.getEncoding();
return enc;
}
}
我们的服务器需要 Latin-1 中的默认字符集来处理旧协议(protocol)中的一些混合编码 (ANSI/Latin-1/UTF-8)。所以我们所有的服务器都使用这个 JVM 参数运行,
-Dfile.encoding=ISO-8859-1
这是 Java 5 上的结果,
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=UTF-8
Default Charset in Use=ISO8859_1
有人试图通过在代码中设置 file.encoding 来更改编码运行时。我们都知道这是行不通的。但是,这显然会抛出 defaultCharset(),但不会影响 OutputStreamWriter 使用的真正默认字符集。
这是错误还是功能?
编辑:接受的答案显示了问题的根本原因。基本上,您不能信任 Java 5 中的 defaultCharset(),这不是 I/O 类使用的默认编码。看起来 Java 6 纠正了这个问题。
最佳答案
这真的很奇怪...一旦设置,默认的 Charset 就会被缓存,并且当类在内存中时它不会更改。使用 System.setProperty("file.encoding", "Latin-1");
设置 "file.encoding"
属性没有任何作用。每次调用 Charset.defaultCharset()
时,它都会返回缓存的字符集。
这是我的结果:
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=ISO-8859-1
Default Charset in Use=ISO8859_1
我使用的是 JVM 1.6。
(更新)
好的。我确实用 JVM 1.5 重现了你的错误。
查看 1.5 的源代码,未设置缓存的默认字符集。我不知道这是否是一个错误,但 1.6 更改了这个实现并使用了缓存的字符集:
JVM 1.5:
public static Charset defaultCharset() {
synchronized (Charset.class) {
if (defaultCharset == null) {
java.security.PrivilegedAction pa =
new GetPropertyAction("file.encoding");
String csn = (String) AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
return cs;
return forName("UTF-8");
}
return defaultCharset;
}
}
JVM 1.6:
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
java.security.PrivilegedAction pa =
new GetPropertyAction("file.encoding");
String csn = (String) AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
当你将文件编码设置为 file.encoding=Latin-1
下一次调用 Charset.defaultCharset()
时,会发生什么,因为缓存的默认值未设置字符集,它将尝试为名称 Latin-1
找到适当的字符集。未找到此名称,因为它不正确,并返回默认的 UTF-8
。
至于OutputStreamWriter
等IO类为什么会返回意外结果,sun.nio.cs.StreamEncoder
(这些 IO 类使用 witch)的实现对于 JVM 1.5 和 JVM 1.6 也是不同的。 JVM 1.6 实现基于 Charset.defaultCharset()
方法来获取默认编码,如果没有提供给 IO 类的话。 JVM 1.5 实现使用不同的方法 Converters.getDefaultEncodingName();
来获取默认字符集。此方法使用自己的缓存,缓存在 JVM 初始化时设置的默认字符集:
JVM 1.6:
public static StreamEncoder forOutputStreamWriter(OutputStream out,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
String csn = charsetName;
if (csn == null)
csn = Charset.defaultCharset().name();
try {
if (Charset.isSupported(csn))
return new StreamEncoder(out, lock, Charset.forName(csn));
} catch (IllegalCharsetNameException x) { }
throw new UnsupportedEncodingException (csn);
}
JVM 1.5:
public static StreamEncoder forOutputStreamWriter(OutputStream out,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
String csn = charsetName;
if (csn == null)
csn = Converters.getDefaultEncodingName();
if (!Converters.isCached(Converters.CHAR_TO_BYTE, csn)) {
try {
if (Charset.isSupported(csn))
return new CharsetSE(out, lock, Charset.forName(csn));
} catch (IllegalCharsetNameException x) { }
}
return new ConverterSE(out, lock, csn);
}
但我同意这些评论。您不应依赖此属性。这是一个实现细节。
关于java - 如何在 Java 中查找默认字符集/编码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1749064/
我是一名优秀的程序员,十分优秀!