Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/380.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中找到默认字符集/编码?_Java_Encoding_Character Encoding - Fatal编程技术网

如何在Java中找到默认字符集/编码?

如何在Java中找到默认字符集/编码?,java,encoding,character-encoding,Java,Encoding,Character Encoding,显而易见的答案是使用Charset.defaultCharset(),但我们最近发现这可能不是正确的答案。有人告诉我,结果与java.io类多次使用的实际默认字符集不同。看起来Java保留了2组默认字符集。有人对这个问题有什么见解吗 我们能够重现一个失败案例。这是一种用户错误,但它仍然可能暴露所有其他问题的根本原因。这是密码 public class CharSetTest { public static void main(String[] args) { Syste

显而易见的答案是使用
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;
    }
}
我们的服务器需要拉丁语-1的默认字符集来处理遗留协议中的一些混合编码(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使用的实际默认字符集

这是一个bug还是一个特性


编辑:接受的答案显示问题的根本原因。基本上,您不能信任Java 5中的defaultCharset(),这不是I/O类使用的默认编码。看起来Java6解决了这个问题。

这真的很奇怪。。。一旦设置,默认的字符集将被缓存,并且当类在内存中时,它不会被更改。使用
System.setProperty(“file.encoding”、“拉丁语-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
不过我使用的是JVM1.6

(更新)

嗯。我确实用JVM1.5复制了您的bug

查看1.5的源代码,没有设置缓存的默认字符集。我不知道这是否是一个bug,但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;
    }
}
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);
}
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;
}
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);
}
下次调用
Charset.defaultCharset()
时,将文件编码设置为
file.encoding=Latin-1
时,会发生的情况是,由于未设置缓存的默认字符集,它将尝试为名称
Latin-1
查找适当的字符集。找不到此名称,因为它不正确,并返回默认的
UTF-8

至于为什么像
OutputStreamWriter
这样的IO类会返回意外的结果,
对于JVM 1.5和JVM 1.6,
sun.nio.cs.streamncoder
(这些IO类使用的开关)的实现也不同。JVM 1.6实现基于
Charset.defaultCharset()
方法来获取默认编码(如果没有提供给IO类)。JVM1.5实现使用不同的方法
Converters.getDefaultEncodingName()
以获取默认字符集。此方法使用JVM初始化时设置的默认字符集的自身缓存:

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;
}
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 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;
    }
}
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);
}
但我同意这些评论。您不应依赖此属性。这是一个实现细节

这是一个bug还是一个特性

看起来像是未定义的行为。我知道,在实践中,可以使用命令行属性更改默认编码,但我不认为这样做时会发生什么

设置此属性时出现问题:

这不是一个bug。J2SE不需要“file.encoding”属性 平台规范;这是Sun实现和应用的内部细节 不应由用户代码检查或修改。它还打算 只读;技术上不可能支持此属性的设置 在命令行上或在程序运行期间的任何其他时间设置为任意值 执行

更改VM和运行时使用的默认编码的首选方法 系统将在启动应用程序之前更改基础平台的区域设置 Java程序

当我看到人们在命令行上设置编码时,我会畏缩——你不知道这会影响到什么代码


如果您不想使用默认编码,请首先通过适当的方法显式设置您想要的编码/

,拉丁语-1与ISO-8859-1相同,因此,默认值对您来说已经可以了。对吧?

使用命令行参数成功地将编码设置为ISO-8859-1。您还以编程方式将其设置为“Latin-1”,但这不是Java文件编码的公认值。看

这样做时,从源代码看,字符集似乎重置为UTF-8。这至少解释了大部分行为

我不知道OutputStreamWriter为什么显示ISO8859_1。它委托给封闭源代码的sun.misc.*类。我猜它不是通过同样的机制来处理编码,这很奇怪


当然,您应该始终指定此代码中的编码含义。我从不依赖平台默认设置。

这种行为其实并不奇怪。从类的实现来看,它是由以下原因造成的:

  • Charset.defaultCharset()
    未在Java 5中缓存已确定的字符集
  • 设置系统属性“file.encoding”并再次调用
    Charset.defaultCharset()
    会导致对系统属性进行第二次求值,未找到名为“Latin-1”的字符集,因此
    Charset.defaultCharset()
    默认为“UTF-8”
  • 然而,
    OutputStreamWriter
    正在缓存默认字符集,并且可能已经在VM初始化期间使用,因此如果系统属性“file.encoding”在运行时发生更改,则其默认字符集将从
    Charset.defaultCharset()
    转移
正如已经指出的,没有记录VM在这种情况下的行为。
Charset.defaultCharset()
API文档对如何使用默认