用BOM表字符串处理utf-8时Java的行为不一致

时间:2019-03-21 03:45:42

标签: java utf-8

我打开Windows记事本,输入18,然后将文件另存为utf-8编码。我知道,我的文件将带有BOM表头,并且我的文件是utf-8编码的文件(带有BOM表头)。

问题是,当通过以下代码打印该字符串时:

//str is that string read from the file using StandardCharsets.UTF_8 encoding
System.out.println(str);

在Windows中,我得到了:

?18

但是在Linux中,我得到了:

18

那么为什么Java的行为不同?如何理解?

2 个答案:

答案 0 :(得分:1)

BOM是零宽度的空间,因此原则上不可见。

但是,Window没有UTF-8编码,但是使用了许多单字节编码之一。从String到输出的转换会将字符集中缺少的BOM变成问号。

记事本仍然可以识别BOM并显示UTF-8文本。

当今的Linux通常使用UTF-8,因此在控制台中也没有问题。


进一步的解释

在Windows System.out上使用控制台,例如,该控制台用作实例Cp-850(约256个字符的单字节字符集)的字符集/编码。很有可能是ĉ或BOM字符缺失。如果java字符串包含这些字符,则不能将它们编码为256个可用字符之一。因此,它们将被转换为?

使用CharsetEncoder

String s = ...
CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
if (!encoder.canEncode(s)) {
    System.out.println("A problem");
}

Windows通常也以单字节编码运行,例如Cp-1252。再次为256个字符。但是,编辑器可能会处理几种编码,如果字体可以表示字符(Unicode代码点),则一切正常。

答案 1 :(得分:0)

java的行为相同,FileInputStream不处理bom。

在Windows中,您的文件为file1,十六进制的file1为EF BB BF 31 38

在linux中,您的文件为file2,file2的十六进制为31 38

阅读它们时,您会得到不同的字符串。

我建议您使用记事本++将bom文件转换为无bom文件。

或者您可以使用BOMInputStream