Java中的字符编码

3

我用记事本打开文件,写入“ą”,保存并关闭。

我尝试两种方式读取这个文件。

第一种:

        InputStream inputStream = Files.newInputStream(Paths.get("file.txt"));
        int result = inputStream.read();
        System.out.println(result);
        System.out.println((char) result);

196 Ä

其次:

        InputStream inputStream = Files.newInputStream(Paths.get("file.txt"));
        Reader reader = new InputStreamReader(inputStream);
        int result = reader.read();
        System.out.println(result);
        System.out.println((char) result);

261 ą

问题: 1)在二进制模式下,这个字母被保存为196?为什么不是261? 2)在哪种编码中,这个字母被保存为196?

我试图理解为什么会有差异。


1
你在记事本中保存的单个字符文件使用了哪种编码? - Tim Biegeleisen
Notepad++显示“utf-8 without bom”,但我在utf-8表中没有看到带有代码196的'ą'字母。 - Thomas Banderas
4个回答

4
UTF-8将范围在U+0080 - U+07FF之间的值编码成两个字节,形式为110xxxxx 10xxxxxx(更多详情请参见wiki)。因此,只有xxxxx xxxxxx 11个字节可用于值。 ą被索引为U+0105(indexed as U+0105),其中0105是十六进制值(十进制值为261)。二进制表示如下:
      01       05    (hex)
00000001 00000101    (bin)
     xxx xxxxxxxx <- values for U+0080 - U+07FF range encode only those bits

     001 00000101 <- which means `x` will be replaced by only this part 

因此,UTF-8编码将添加110xxxxx 10xxxxxx掩码,这意味着它将组合起来。
110xxxxx 10xxxxxx
   00100   000101

into (两个字节):

11000100 10000101

现在,InputStream原始字节 的形式读取数据。所以当你第一次调用 inputStream.read(); 时,你会得到 11000100,这是十进制中的 196。第二次调用 inputStream.read(); 将返回 10000101,这是十进制中的 133Reader 在 Java 1.1 中引入,因此我们可以避免代码中出现这种混乱。相反,我们可以指定 Reader 应该使用什么编码(或让它使用默认编码)来获取正确编码的值,就像在这个例子中一样,00000001 00000101(无掩码)等于十六进制的 0105 和 十进制的 261

简而言之

  • 如果您想将数据作为文本读取,请使用具有正确指定编码的Reader
  • 如果您想将数据作为原始字节读取,请使用Stream

1
由于您使用不同的编码方式阅读这两封信,因此您可以通过 InputStreamReader::getEncoding 来检查您的编码方式。
String s = "ą";

char iso_8859_1 = new String(s.getBytes(), "iso-8859-1").charAt(0);
char utf_8 = new String(s.getBytes(), "utf-8").charAt(0);   

System.out.println((int) iso_8859_1 + " " + iso_8859_1);
System.out.println((int) utf_8 + " " + utf_8);

输出结果为

196 Ä
261 ą

0

尝试使用UTF-8编码的InputStreamReader,该编码与从Notepad ++编写文件所使用的编码相匹配:

// this will use UTF-8 encoding by default
BufferedReader in = Files.newBufferedReader(Paths.get("file.txt"));

String str;
if ((str = in.readLine()) != null) {
    System.out.println(str);
}
in.close();

我无法确切地回答你为什么看到了你所看到的输出,但如果你使用错误的编码读取,你不一定看到你保存的内容。例如,如果单个字符ą用两个字节编码,但你按ASCII读取,则可能得到两个字符,这些字符将不匹配你的原始文件。

字符 ą 编码为两个字节 (C4 85),其中 C4 在十进制中为 196。因此,他们确实只读取了第一个字节。 - TiiJ7
@TiiJ7 感谢你的侦探工作。我不知道如何解决这个问题,但编码问题是我首先注意到的 :-) - Tim Biegeleisen

0

您正在获取 十进制 值的 LATIN 字母, 您需要使用 UTF-8 编码标准来保存文件

确保在读取时使用相似的标准。

0x0105 261 LATIN SMALL LETTER A WITH OGONEK ą

0x00C4 196 LATIN CAPITAL LETTER A WITH DIAERESIS �

请参考:https://www.ssec.wisc.edu/~tomw/java/unicode.html


OP已经意识到了这一点。所以,他并不是那个意思。 - Soner from The Ottoman Empire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接