答案显然是使用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的默认字符集来处理在遗留协议的一些混合编码(ANSI /拉丁-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中纠正这个问题。
这真是奇怪......一旦设定,默认字符集被缓存,而类是在内存中不被改变。 设置"file.encoding"
与属性System.setProperty("file.encoding", "Latin-1");
什么也没做。 每次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
至于为什么IO类,如OutputStreamWriter
返回意外的结果,
实施sun.nio.cs.StreamEncoder
(女巫使用由这些IO类)是不同的,以及对于JVM 1.5和1.6 JVM。 在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);
}
但我同意的意见。 你不应该依赖于这个属性 。 这是一个实现细节。
这是一个错误或功能?
看起来不确定的行为。 我知道,在实践中,你可以使用命令行属性更改默认的编码,但我不认为会发生什么,当你做到这一点的定义。
错误ID:4153515在问题设置该属性:
这是不是一个错误。 不需要由J2SE平台规范的“file.encoding的”财产; 它是Sun的实施方式中的内部细节,并且不应被检查或由用户代码修改。 它也旨在为只读; 在技术上不可能支持程序执行时该属性对命令行或在其他任何时间任意值的设置。
改变由VM和运行时系统使用的默认编码的首选方法是在启动Java程序之前更换底层平台的语言环境。
我畏缩,当我看到人们设置的编码在命令行上 - 你不知道会影响到哪些代码。
如果你不想使用默认的编码,设置你通过适当的方法/明确希望的编码构造 。
首先,Latin-1的相同ISO-8859-1,所以,默认为已经为您确定。 对?
您已成功将编码设置为ISO-8859-1您的命令行参数。 您还可以设置它以编程为“Latin-1的”,但是,这不是一个Java文件编码公认的价值。 见http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
当你这样做,看起来像字符集重置为UTF-8,从查看源。 这至少说明了大部分的行为。
我不知道为什么OutputStreamWriter显示ISO8859_1。 它委托给封闭源代码sun.misc。*类。 我猜这是不太用编码通过相同的机制,这是奇怪的处理。
当然,你应该总是指定你在这段代码的意思编码。 我从来没有依赖于平台的默认。
该行为是不是真的那么奇怪。 展望类的实现,它的原因是:
-
Charset.defaultCharset()
没有缓存确定的字符在Java 5中设置。 - 设置系统属性“的file.encoding”并调用
Charset.defaultCharset()
再次导致系统性能的第二次评估,没有字符的名称设置为“Latin-1的”被发现,所以Charset.defaultCharset()
默认为“UTF -8" 。 - 该
OutputStreamWriter
不过是缓存的默认字符集,大概VM初始化过程中已被使用,因此其默认的字符集从转移Charset.defaultCharset()
如果系统属性“file.encoding的”已在运行时被改变。
前面已经指出的那样,它不记录如何使用VM必须在这种情况下的行为。 该Charset.defaultCharset()
API文档是不是默认字符集是如何确定的,只提的是,它通常是做了VM启动的基础上,如OS默认字符集或默认的语言环境因素非常精确。
我已在WAS服务器VM参数为-Dfile.encoding = UTF-8来改变服务器的默认字符集。
校验
System.getProperty("sun.jnu.encoding")
这似乎是同样的编码作为你的系统的命令行中使用的一个。