编码不适用于某些字符
encoding is not working for certain characters
我的文档中只有几个字符,例如 、 和 。当我读取输入流时,我尝试使用 UTF-8 和 UTF-16 作为编码。然后我做了一些处理,比如在浏览器中显示文本,将文本写入输出文件。一切看起来都不错,我看到了相同的角色。
虽然,我也在计算每个单词中的字符数,当我计算这些单词的字符数时,结果是 2。我看了看,在内部,与这些单词对应的 java 字符串就像 ð¿ , 明明是2个字符.
我认为使用 UTF-8 编码应该只会给我准确的字符,但它给了我一些奇怪的东西。知道我可能遗漏了什么
你糊涂了。
字符串没有编码。
字节(所以,byte[]
)有一个编码。字符串没有。
每当您将 byte[]
转换为字符串(并且标准输入是字节,而不是字符串,标准输出也是如此。文件是字节。通过网络传输的数据,除非封装在某些面向字符的协议中,否则是字节),应用编码。
无论何时将字符串转换为字节,都会应用编码。
因此,一旦你有了一个java.lang.String对象,只有两个选择:
- 该字符串是乱七八糟的 gobbledygook,因为您有一些字节,例如ISO-8859-1 编码,但您使用
new String(theBytes, StandardCharsets.UTF_8)
将其转换为字符串。请注意 new String(theBytes)
是 被禁止的 代码并且总是有错误,永远不要调用该方法(使用 'platform default encoding',这是 [=63 的一种有趣的说法=]).解决方法是创建正确的字符串。例如:
String x = "é";
System.out.println(x.length());
byte[] data = x.getBytes(StandardCharsets.UTF_8);
String y = new String(data, StandardCharsets.ISO_8859_1);
System.out.println(y.length());
System.out.println(y);
> 1
> 2
> é
看到了吗?这个过程把数据变成了无意义的垃圾,现在 y 的大小刚刚被打破。除了避免应用错误的编码之外,没有解决这个问题的办法。
或者,
- 您有来自更高 unicode 平面之一的字符。
原来java是在过渡时期创建的,java在内部使用了所谓的代理对编码。这意味着所有 unicode 编号非常高的字符(我相信,超过 32767),实际上由 2 个字符表示,而不是 1。这 2 个字符放在一起,称为代理项对。
解决方案是使用字符串具有的基于代码点的方法:
String simple = "é";
System.out.println(simple.length());
System.out.println(simple.codePointCount(0, simple.length()));
> 1
> 1
String complex = "";
System.out.println(complex.length());
System.out.println(complex.codePointCount(0, complex.length()));
> 2
> 1
.length()
returns 'unicode char units' 的数量,它将代理对表示的 unicode 代码点计算为 2 个单位。 codePointCount
方法将它们计为 1 个 unicode 代码点。
I have few characters in my document like , , and
因此,如果其中一个字符具有足够高的代码点,它需要一个代理对来表示它,瞧。这解释了它 - 使用 codePointCount
而不是 length
(请注意 codePointCount
的性能特征是 O(n) - 长字符串需要更长的时间,因为 java 必须遍历整个事物以找到代理对并相应地调整计数)。
我的文档中只有几个字符,例如 、 和 。当我读取输入流时,我尝试使用 UTF-8 和 UTF-16 作为编码。然后我做了一些处理,比如在浏览器中显示文本,将文本写入输出文件。一切看起来都不错,我看到了相同的角色。 虽然,我也在计算每个单词中的字符数,当我计算这些单词的字符数时,结果是 2。我看了看,在内部,与这些单词对应的 java 字符串就像 ð¿ , 明明是2个字符.
我认为使用 UTF-8 编码应该只会给我准确的字符,但它给了我一些奇怪的东西。知道我可能遗漏了什么
你糊涂了。
字符串没有编码。
字节(所以,byte[]
)有一个编码。字符串没有。
每当您将 byte[]
转换为字符串(并且标准输入是字节,而不是字符串,标准输出也是如此。文件是字节。通过网络传输的数据,除非封装在某些面向字符的协议中,否则是字节),应用编码。
无论何时将字符串转换为字节,都会应用编码。
因此,一旦你有了一个java.lang.String对象,只有两个选择:
- 该字符串是乱七八糟的 gobbledygook,因为您有一些字节,例如ISO-8859-1 编码,但您使用
new String(theBytes, StandardCharsets.UTF_8)
将其转换为字符串。请注意new String(theBytes)
是 被禁止的 代码并且总是有错误,永远不要调用该方法(使用 'platform default encoding',这是 [=63 的一种有趣的说法=]).解决方法是创建正确的字符串。例如:
String x = "é";
System.out.println(x.length());
byte[] data = x.getBytes(StandardCharsets.UTF_8);
String y = new String(data, StandardCharsets.ISO_8859_1);
System.out.println(y.length());
System.out.println(y);
> 1
> 2
> é
看到了吗?这个过程把数据变成了无意义的垃圾,现在 y 的大小刚刚被打破。除了避免应用错误的编码之外,没有解决这个问题的办法。
或者,
- 您有来自更高 unicode 平面之一的字符。
原来java是在过渡时期创建的,java在内部使用了所谓的代理对编码。这意味着所有 unicode 编号非常高的字符(我相信,超过 32767),实际上由 2 个字符表示,而不是 1。这 2 个字符放在一起,称为代理项对。
解决方案是使用字符串具有的基于代码点的方法:
String simple = "é";
System.out.println(simple.length());
System.out.println(simple.codePointCount(0, simple.length()));
> 1
> 1
String complex = "";
System.out.println(complex.length());
System.out.println(complex.codePointCount(0, complex.length()));
> 2
> 1
.length()
returns 'unicode char units' 的数量,它将代理对表示的 unicode 代码点计算为 2 个单位。 codePointCount
方法将它们计为 1 个 unicode 代码点。
I have few characters in my document like , , and
因此,如果其中一个字符具有足够高的代码点,它需要一个代理对来表示它,瞧。这解释了它 - 使用 codePointCount
而不是 length
(请注意 codePointCount
的性能特征是 O(n) - 长字符串需要更长的时间,因为 java 必须遍历整个事物以找到代理对并相应地调整计数)。