Java 2字节中的字符大小不是?

Shr*_*ath 51 java string char

我曾经从文本文件中RandomAccessFile读取过byte.

public static void readFile(RandomAccessFile fr) {
    byte[] cbuff = new byte[1];
    fr.read(cbuff,0,1);
    System.out.println(new String(cbuff));
}
Run Code Online (Sandbox Code Playgroud)

为什么我看到一个完整的角色被这个阅读?

Joa*_*uer 114

A char表示Java (*)中的字符.它大2字节(至少是有效值范围所暗示的).

这并不一定意味着字符的每个表示都是2个字节长.实际上,许多编码仅为每个字符保留1个字节(或者对于最常见的字符使用1个字节).

当您调用String(byte[])构造函数时,您要求Java 使用平台默认编码将其转换byte[]为a String.由于平台默认编码通常是1字节编码(如ISO-8859-1)或可变长度编码(如UTF-8),因此可以轻松地将该1字节转换为单个字符.

如果您在使用UTF-16(或UTF-32或UCS-2或UCS-4或...)作为平台默认编码的平台上运行该代码,那么您将无法获得有效的结果(您将获得一个String含有Unicode替换字符代替).

这就是为什么你不应该依赖于平台的默认编码的原因之一:之间进行转换时byte[]char[]/ String或之间InputStreamReader之间或OutputStreamWriter,你应该总是指定要使用的编码.如果不这样做,那么您的代码将依赖于平台.

(*)这并不完全正确:a char表示UTF-16码点.任一个2 UTF-16的码点代表一个Unicode码点.Unicode代码点通常表示一个字符,但有时会使用多个Unicode代码点来组成一个字符.但上面的近似值足以讨论手头的主题.

  • @Mikaveli:没有.Java中的`char`总是**长2个字节.您可能知道,有Unicode代码点> 2 ^ 16.为了表示`String`中的那些,Java使用2个`char`值(低代理和高代理).这意味着`String`实际上是UTF-16编码的.但这个事实超出了这个问题的范围. (32认同)
  • @Mikaveli:这个讨论超出了问题的范围,但并不完全:Unicode**代码点*从`U + 0000`变为'U + 10FFFF`(并非所有这些都被使用,有些被声明*永远不会被使用.Java中的`char`可以将值"U + 0000"变为"U + FFFF".要表示Unicode代码点>`U + FFFF`,您需要使用两个相邻的`char`值(一个在低代理范围(U + DC00..U + DFFF)中,一个在高代理范围内(U + D800) ..U + DBFF)). (3认同)
  • @Mikaveli:是的,但在某种程度上与问题无关:问题实际上不是关于Java中文本的内部表示(与标题所暗示的相反),而是关于将单个字节转换为有效字符,这可以很容易地解释,而无需详细介绍 Java 中文本数据的存储(并且解释答案中的所有内容只会使问题更加混乱)。 (2认同)

Mic*_*ael 13

Java将其内部的所有"字符"存储为两个字节.但是,当它们成为字符串等时,字节数将取决于您的编码.

某些字符(ASCII)是单字节,但许多其他字符是多字节的.

Java支持Unicode,因此根据:

Java角色文档

支持的最大值是"\ uFFFF"(十六进制FFFF,十六进制65535)或11111111 11111111二进制(两个字节).

  • \uFFFF如何证明字符可以是1-4个字节?0xFFFF 是 2 个字节。另外:U+FFFF **不是**最高的 Unicode 代码点,还有更大的代码点。 (2认同)

And*_*s_D 6

构造函数String(byte[] bytes)从缓冲区中获取字节并将它们编码为字符.

它使用平台默认字符集将字节编码为字符.如果您知道,您的文件包含在不同的字符集中编码的文本,您可以使用它String(byte[] bytes, String charsetName)来使用正确的编码(从字节到字符).