我使用
RandomAccessFile
从文本文件中读取 byte
。
public static void readFile(RandomAccessFile fr) {
byte[] cbuff = new byte[1];
fr.read(cbuff,0,1);
System.out.println(new String(cbuff));
}
为什么我看到它正在读取一个完整的字符?
A
char
代表 Java 中的字符 (*)。它有 2 个字节大(或 16 位)。
这并不一定意味着字符的每个表示形式都是 2 个字节长。事实上,许多字符编码只为每个字符保留 1 个字节(或为最常见的字符使用 1 个字节)。
String(byte[])
构造函数时,您要求Java使用平台的默认字符集(Java 18之前)
(**)将
byte[]
转换为String
。由于平台默认字符集通常是 1 字节编码(例如 ISO-8859-1)或可变长度编码(例如 UTF-8),因此它可以轻松地将 1 字节转换为单个字符。
如果您在使用 UTF-16(或 UTF-32 或 UCS-2 或 UCS-4 或...)作为平台默认编码的平台上运行该代码,那么您将不会获得有效的结果(您将获取包含 Unicode 替换字符的
String
)。
这就是为什么您不应该依赖平台默认编码的原因之一:在
byte[]
和 char[]
/String
之间或在 InputStream
和 Reader
之间或在 OutputStream
和 Writer
之间进行转换时,你应该总是指定哪种编码你想使用。如果不这样做,那么您的代码将依赖于平台。
(*) 这并不完全正确:char
代表一个
UTF-16代码单元。 one 或 two UTF-16 代码单元表示 Unicode 代码点。一个 Unicode 代码点“通常”代表一个字符,但有时使用多个 Unicode 代码点来组成单个字符。但上面的近似值足以讨论当前的主题。 (**) 请注意,在 Android 上,默认字符集始终为 UTF-8,从 Java 18 开始,Java 平台本身也
切换到此默认值(但仍然可以配置为以旧方式运行) ) Java 在内部将所有“字符”存储为两个字节。但是,当它们变成字符串等时,字节数将取决于您的编码。
它使用平台默认字符集将字节编码为字符。如果您知道您的文件包含以不同字符集编码的文本,您可以使用
String(byte[] bytes, String charsetName)
使用正确的编码(从字节到字符)。
在 ASCII 文本文件中,每个字符只是一个字节
看起来您的文件包含 ASCII 字符,这些字符仅用 1 个字节进行编码。如果文本文件包含非 ASCII 字符,例如2 字节 UTF-8,那么您只得到第一个字节,而不是整个字符。
这里有一些很好的答案,但我想指出 jvm 可以自由地在任何大小的空间 >= 2 字节中存储 char 值。
Java char 是 2 个字节。但文件编码可能不同。
如果文件的编码是 UTF-16,如果您的 UTF-16 是小端,它仍然可能会显示正确的字符。例如,A 的小端 UTF-16 为 [65, 0]。那么当你读取第一个字节时,它返回 65。第二个字节用 0 填充后,你将得到 A。
String s = "ABCÃࠁ";
byte[] bytes = s.getBytes();
char[] charArray = s.toCharArray();
在上面,字符串s
中的最后一个字符,也就是
charArray
中的最后一个元素,用3个字节“表示”。如果我们序列化
charArray
,我们会看到以下字节(每个 char
2 个字节)
00 41 00 42 00 43 00 C3 08 01