【问题标题】:Why does Java's String.getBytes() uses "ISO-8859-1"为什么 Java 的 String.getBytes() 使用“ISO-8859-1”
【发布时间】:2012-09-21 11:37:15
【问题描述】:

来自 java.lang.StringCoding :

String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;

这是从 Java.lang.getBytes() 使用的,在 linux jdk 7 我一直认为 UTF-8 是默认字符集?

谢谢

【问题讨论】:

  • 它没有 - 请参阅下面的答案
  • 编码很难预测——Centos 6 和 Centos 7 以及 Oracle JDK 和 Open JDK 上的字符集不同——你永远不应该依赖默认字符集。我不明白为什么有人会期待 UTF-8,即使它如此受欢迎 - 我相信 Java 在内部使用 UTF-16。

标签: java utf-8 character-encoding iso-8859-1


【解决方案1】:

这是出于兼容性原因。

从历史上看,Windows 和 Unix 上所有未指定字符集的 java 方法当时都使用通用的方法,即 "ISO-8859-1"

正如 Isaac 和 javadoc 所提到的,使用默认的平台编码(参见Charset.java):

594    public static Charset defaultCharset() {
595        if (defaultCharset == null) {
596            synchronized (Charset.class) {
597                String csn = AccessController.doPrivileged(
598                    new GetPropertyAction("file.encoding"));
599                Charset cs = lookup(csn);
600                if (cs != null)
601                    defaultCharset = cs;
602                else
603                    defaultCharset = forName("UTF-8");
604            }
605        }
606        return defaultCharset;
607    }

在进行字符串到字节或字节到字符串的转换时始终指定字符集。

即使在 String.getBytes() 的情况下,您仍然会发现不推荐使用字符集的方法(其中大多数在 Java 1.1 出现时已被弃用)。就像字节序一样,平台格式无关紧要,重要的是存储格式的规范。

【讨论】:

  • 不完全正确。在 IBM 的 OS/390(后来称为 z/OS)上,文本文件以 EBCDIC 而不是 ASCII 编码;因此默认的平台编码不是 ISO-8859-1,而是一些基于 EBCDIC 的编码(比如 EBCDIC 0037)。
  • AFAIK 不采用字符集的方法不会被弃用,它们应该只使用默认字符集,不是吗?我知道这可能是一个“遗留”代码,这不应该是一个棘手的错误吗?
【解决方案2】:

有点复杂……

Java 尝试使用默认字符编码来使用 String.getBytes() 返回字节。

  • 默认字符集由系统 file.encoding 属性提供。
  • 这是缓存的,在 JVM 启动后通过 System.setProperty(..) 更改它是没有用的。
  • 如果 file.encoding 属性未映射到已知字符集,则指定 UTF-8。

....这是棘手的部分(可能永远不会发挥作用)....

如果系统无法使用默认字符集(UTF-8 或其他字符集)对字符串进行解码或编码,则会回退到 ISO-8859-1。如果回退不起作用......系统将失败!

....真的...(喘气!)...如果我指定的字符集无法使用,并且 UTF-8 或 ISO-8859-1 也无法使用,它会崩溃吗?

是的。 StringCoding.encode(...)方法中的Java源码cmets状态:

// 如果我们找不到 ISO-8859-1(必需的编码),则说明安装存在严重问题。

...然后它调用 System.exit(1)


那么,为什么在 getBytes() 方法中有意回退到 ISO-8859-1?

用户 JVM 可能不支持 UTF-8 或 JVM 启动时指定的字符集的解码和编码,但可能性不大。

那么,在 getBytes() 期间,String 类中是否正确使用了默认字符集?

没有。然而,更好的问题是......


String.getBytes() 是否兑现了它的承诺?

Javadoc 中定义的合同是正确的。

当这个字符串不能被编码时这个方法的行为 默认字符集未指定。 CharsetEncoder 类应该是 当需要对编码过程进行更多控制时使用。


好消息(以及更好的做事方式)

始终建议在将字节转换为字符串时明确指定“ISO-8859-1”或“US-ASCII”或“UTF-8”或任何您想要的字符集,反之亦然——除非——你之前已经获得了默认字符集,并且 100% 确定它是您需要的。

改用这个方法:

public byte[] getBytes(String charsetName)

要查找系统的默认设置,只需使用:

Charset.defaultCharset()

希望对您有所帮助。

【讨论】:

  • 如果您遵循 getBytes() 的流程(未提供聊天集),您将看到它正在尝试获取默认字符集,如果未找到则返回“UTF-8”,但如您所见从上面的代码中,stringcodes 中有一个不同的逻辑,如果未提供默认为 ISO-8859-1,这是一个冲突......我知道你可以传递字符集问题是为什么它不默认为 utf-8
  • 该行为在 javadoc 中指定。我会修改我的答案以清楚地发布它。
  • 不是 :) 这就是我的观点,javadoc 指出:“使用平台的默认字符集将此字符串编码为字节序列,将结果存储到新的字节数组中。”这就是问题所在,在某些情况下 Java 不会使用默认字符集
  • 你说得很好。因为如果有一个默认的字符集,那么它应该使用它来进行转换。你,我的朋友,发现了一个错误!
  • 也许删除你的最后一条评论呢? :D
【解决方案3】:

无参数String.getBytes() 方法默认使用 ISO-8859-1。如果可以确定,它将使用默认平台编码。但是,如果该编码丢失或者是无法识别的编码,则它会回退到 ISO-8859-1 作为“默认默认值”。

您应该非常在实践中很少看到这种情况。通常会正确检测到平台默认编码。

但是,我强烈建议您在每次执行编码或解码操作时指定明确的字符编码。即使您想要平台默认值,也要明确指定。

【讨论】:

    【解决方案4】:

    详细说明 Skeet 的答案(这当然是正确的)

    java.lang.String 的源getBytes() 中调用StringCoding.encode(char[] ca, int off, int len),它的第一行有:

    String csn = Charset.defaultCharset().name();
    

    然后(不是立即但绝对)它调用 static byte[] StringEncoder.encode(String charsetName, char[] ca, int off, int len) 您引用的行来自哪里 - 作为 charsetName 传递 csn - 所以在这一行中 charsetName 是默认字符集如果存在的话。

    【讨论】:

      猜你喜欢
      • 2015-07-07
      • 2011-01-15
      • 2015-10-11
      • 2011-06-10
      • 2016-02-19
      • 1970-01-01
      • 2015-07-07
      • 2014-08-21
      • 2019-02-21
      相关资源
      最近更新 更多