首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >从编译到运行时,Java字符串编码到底是如何工作的

从编译到运行时,Java字符串编码到底是如何工作的
EN

Stack Overflow用户
提问于 2010-01-30 04:06:09
回答 4查看 8.9K关注 0票数 20

我最近意识到我并不完全理解Java的字符串编码过程。

考虑以下代码:

public class Main
{
    public static void main(String[] args)
    {
        System.out.println(java.nio.charset.Charset.defaultCharset().name());
        System.out.println("ack char: ^"); /* where ^ = 0x06, the ack char */
    }
}

因为控制字符是interpreted differently between windows-1252 and ISO-8859-1,所以我选择ack字符进行测试。

我现在用不同的文件编码、UTF-8、windows-1252ISO-8859-1编译它。两者编译成完全相同的东西,逐个字节地编译,就像md5sum验证的那样。

然后我运行这个程序:

$ java Main | hexdump -C
00000000  55 54 46 2d 38 0a 61 63  6b 20 63 68 61 72 3a 20  |UTF-8.ack char: |
00000010  06 0a                                             |..|
00000012

$ java -Dfile.encoding=iso-8859-1 Main | hexdump -C
00000000  49 53 4f 2d 38 38 35 39  2d 31 0a 61 63 6b 20 63  |ISO-8859-1.ack c|
00000010  68 61 72 3a 20 06 0a                              |har: ..|
00000017

$ java -Dfile.encoding=windows-1252 Main | hexdump -C
00000000  77 69 6e 64 6f 77 73 2d  31 32 35 32 0a 61 63 6b  |windows-1252.ack|
00000010  20 63 68 61 72 3a 20 06  0a                       | char: ..|
00000019

无论使用哪种编码,它都会正确地输出0x06

好的,它仍然输出相同的0x06,这将被windows-1252代码页解释为可打印的确认字符。

这就引出了几个问题:

  1. 正在编译的Java文件的代码页/字符集是否期望与正在编译它的系统的默认字符集相同?这两个always synonymous?
  2. The编译表示看起来并不依赖于编译时字符集,真的是这样吗?

这是否意味着,如果不为当前的Java使用标准字符,那么在运行时,它们可能会被不同的解释?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-01-30 04:21:59

  1. 源文件可以是任何编码
  2. 你需要告诉编译器源文件的编码(如javac -encoding...);否则,假定平台编码在类文件二进制文件中为UTF-8,字符串文字存储为(修改后的)UTF-8,但除非您使用字节码,否则这并不重要(请参见JVM spec)
  3. Strings

UTF-16,always (请参见Java language spec)

  1. The System.out PrintStream将字符串从UTF-16转换为系统编码中的字节,然后再将其写入标准输出

备注:

票数 24
EN

Stack Overflow用户

发布于 2010-01-30 05:35:28

关于Java中的字符串编码的“要知道的内容”的摘要:

在内存中,String实例是一系列16位的“代码单元”,将其作为Java值进行处理。从概念上讲,这些代码单元编码一系列“代码点”,其中代码点是“根据Unicode标准属于给定字符的数字”。代码点的范围从0到一百万多一点,尽管到目前为止只定义了10万个左右。从0到65535的代码点被编码到一个代码单元中,而其他代码点使用两个代码单元。此过程称为UTF-16 (也称为UCS-2)。有一些微妙之处(一些代码点是无效的,例如65535,在前65536个代码点中有2048个代码点的范围,正好为其他代码点的编码保留)。

  • 代码页等不会影响

  • 中存储字符串的方式。这就是"Unicode“以"Uni”开头的原因。只要您不对字符串执行I/O,您就处于Unicode的世界中,在这里,每个人都使用相同的字符映射来编码points.
  • Charsets,当将字符串编码为字节或从字节解码字符串时,就会开始行动。除非明确指定,否则Java将使用取决于用户"locale“的默认字符集,”locale“是日本计算机说日语的模糊集合概念。当您使用System.out.println()打印字符串时,JVM会将字符串转换为适合这些字符所在位置的内容,这通常意味着使用取决于当前语言环境(或JVM对当前语言环境的猜测)的字符集将它们转换为字节。
  • One Java应用程序是Java编译器。Java编译器需要解释源文件的内容,在系统级,这些内容只是一串字节。然后,Java编译器会为此选择一个默认字符集,并根据当前的语言环境执行此操作,就像Java所做的那样,因为Java编译器本身就是用Java编写的。Java编译器(javac)接受命令行标志(-encoding),该标志可用于覆盖默认选择。
  • Java编译器生成独立于语言环境类文件。字符串最终以(某种程度上) UTF-8编码出现在这些类文件中,而不管Java编译器用来解释源文件的字符集是什么。运行Java编译器的系统上的区域设置会影响如何解释源代码,但是一旦Java编译器了解到您的字符串包含代码点编号6,那么这个代码点就会进入类文件,而不是其他代码点。请注意,代码点0到127具有相同的UTF-8、CP-1252和ISO-8859-1编码,因此您获得的结果不足为奇。
  • 即使String实例不依赖于任何类型的编码,只要它们保留在内存中,您可能希望对字符串执行的一些操作是与区域设置相关的。这不是编码的问题;但是语言环境也定义了一种“语言”,因此,大写和小写的概念取决于所使用的语言。通常的疑点是调用"unicode".toUpperCase():这会生成"UNICODE",除非当前语言环境是土耳其语,在这种情况下,您会得到"UNİCODE" ( "I“有一个点)。这里的基本假设是,如果当前语言环境是土耳其语,那么应用程序管理的数据可能是土耳其语文本;就我个人而言,我觉得这个假设充其量是有问题的。但事实就是如此。

实际上,您应该在代码中显式地指定编码,至少在大多数情况下是这样。不要调用String.getBytes(),调用String.getBytes("UTF-8")。在将默认的依赖于区域设置的编码应用于与用户交换的某些数据(如配置文件或立即显示的消息)时,可以使用该编码;但在其他地方,请尽可能避免使用依赖于区域设置的方法。

在Java的其他依赖于语言环境的部分中,还有日历。有整个时区业务,它依赖于“时区”,它应该与计算机的地理位置相关(这不是严格意义上的“地区”的一部分……)。此外,无数的Java应用程序在曼谷运行时神秘地失败,因为在泰国地区,Java默认使用佛教日历,根据该日历,当前年份是2553。

根据经验,假设世界是广阔的(的确如此!)并且保持通用(不要做任何依赖于字符集的事情,直到最后一刻,那时必须实际执行I/O )。

票数 15
EN

Stack Overflow用户

发布于 2010-01-30 04:10:50

如果使用不同的编码进行编译,则这些编码只会影响源文件。如果源代码中没有任何特殊字符,那么结果字节码将不会有任何差异。

对于运行时,使用操作系统的默认字符集。这与您用于编译的字符集无关。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2164804

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档