gpt4 book ai didi

java - 如何在 Java 中查找默认字符集/编码?

转载 作者:IT老高 更新时间:2023-10-28 11:36:05 26 4
gpt4 key购买 nike

显而易见的答案是使用 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com