编码不适用于某些字符

encoding is not working for certain characters

我的文档中只有几个字符,例如 、 和 。当我读取输入流时,我尝试使用 UTF-8 和 UTF-16 作为编码。然后我做了一些处理,比如在浏览器中显示文本,将文本写入输出文件。一切看起来都不错,我看到了相同的角色。 虽然,我也在计算每个单词中的字符数,当我计算这些单词的字符数时,结果是 2。我看了看,在内部,与这些单词对应的 java 字符串就像 ð¿ , 明明是2个字符.

我认为使用 UTF-8 编码应该只会给我准确的字符,但它给了我一些奇怪的东西。知道我可能遗漏了什么

你糊涂了。

字符串没有编码。

字节(所以,byte[])有一个编码。字符串没有。

每当您将 byte[] 转换为字符串(并且标准输入是字节,而不是字符串,标准输出也是如此。文件是字节。通过网络传输的数据,除非封装在某些面向字符的协议中,否则是字节),应用编码。

无论何时将字符串转换为字节,都会应用编码。

因此,一旦你有了一个java.lang.String对象,只有两个选择:

  1. 该字符串是乱七八糟的 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 的大小刚刚被打破。除了避免应用错误的编码之外,没有解决这个问题的办法。

或者,

  1. 您有来自更高 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 必须遍历整个事物以找到代理对并相应地调整计数)。