Java UTF-8差异

8

1
使用readUTF在数据流上获取(真实的)Unicode字符串。 - hakre
@hakre,谢谢。但我不认为我能改变那个程序的行为,我只能处理它给我的东西。 - Prof. Falken
你遇到的问题是由DataOutputStream写入的数据还是由DataInputStream读取的数据引起的? - Matt Ball
1
它是由DataOutputStream编写的。然后我尝试在一个(据说)支持UTF-8的C程序中读取它,这是我正在攻击的程序。@Matt Ball - Prof. Falken
1
Java 生成这些内容的无效 UTF-8。严格解释必须防范此问题。这是一个问题。 - tchrist
显示剩余2条评论
5个回答

16
在C语言中,字符串以字节值00作为终止符。问题在于,在Java字符串中,您可以有0字符,但是为了避免在传递字符串到C(所有本地方法都是用C编写的)时出现混淆,该字符以另一种方式编码,即两个字节。
11000000 10000000
根据Javadoc的说明,这两个字符实际上都不是00。这是一个绕过不能轻易更改的东西的技巧。还要注意,这是有效的UTF-8,并且可以正确解码为00。

谢谢!这确实回答了我的问题。不幸的是,它没有给我任何关于为什么我的程序失败的见解,但我会继续寻找。 :-) - Prof. Falken
2
我不确定this is valid UTF-8是否合法 - 一个天真的解码器会将它解码为0,但RFC 3629清楚地指出:*上述解码算法的实现必须保护不允许解码无效序列。例如,天真的实现可能会将过度长度的UTF-8序列C0 80解码为字符U+0000 [...]. - Paŭlo Ebermann
@Paŭlo Ebermann,嗯?你是什么意思?overlong是什么意思? - Prof. Falken
5
更清楚地说,3.1版本之前的UTF-8是合法的。从3.1版本开始,每个字符必须使用最短的编码形式进行编码。这也在Unicode标准的当前版本6.0中的第93f页中定义为UTF-8(http://www.unicode.org/versions/Unicode6.0.0/,章节“符合性”)。UTF-8序列映射到代理项是无效的。 - Paŭlo Ebermann
2
还有其他语言也使用这个,我知道Tcl也是,我想应该还有更多。 - RHSeeger

4

没有“嵌入的空字符”意味着原始数据不包含单个 0x00(NULL)字节。

\u0000 被编码为(二进制) 11000000 10000000,(十六进制)0xC080


1

这不是Java的普遍差异,只存在于DataInput/OutputStream中。如果字符串数据是使用DataOutputStream写入的,则只需使用DataInputStream读取即可。

如果您需要将字符串数据写入文件,不要使用DataOutputStream,而应该使用Writer,它适用于字符流。


谢谢,但我现在不能更改Java程序,只能处理它创建的输出。 - Prof. Falken
确实。如果我有机会更新所有其他客户端,我可能能够改变这一点。 - Prof. Falken

1

这仅适用于DataOutputStream的writeUTF方法,而不适用于普通转换流(如OutputStreamWriter等)。

这意味着,如果您有一个字符串"\u0000",它将被编码为0xC0 0x80,而不是简单的0x00

另外,在另一方面,这个序列0xB0 0x80,在正常的UTF-8字符串中永远不会出现,代表一个空字符。

此外,您链接的文档似乎是Unicode仍然是16位字符集的时候 - 现在它也允许超过0xFFFF的字符,每个字符将由两个Java char值表示(以UTF-16格式,代理对),并且如果我计算正确,需要4个字节的UTF-8。不过我不确定这里的实现 - 它看起来像是用CESU-8格式编写的(例如,两个3字节序列,每个序列对应一个UTF-16代理,它们一起给出一个Unicode字符)。您也需要注意这一点。

如果您正在使用Java,最简单的方法是使用DataInputStream将其读入字符串,然后使用getBytes("UTF-8")或OutputStreamWriter将其转换为真正的UTF-8数据。

谢谢,很有趣。还要感谢介绍CESU-8,我之前不知道这个缩写。 - Prof. Falken
此外,就我们的目的而言,我们从未超出16位字符集。 - Prof. Falken
你的意思是“用Java char值表示”吗?如果是这样,我从来没有理解过,因为char只有16位,对吧? - Prof. Falken
应该由两个Java字符值表示。谢谢注意。 - Paŭlo Ebermann
1
OP链接的JavaDocs来自Java 1.4.2。这里是最新的:http://download.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8 - Matt Ball
+1 好的,如果你花时间写它,我可以读懂它,对吧? :-) - Prof. Falken

0
如果你有困难读取一个“保存”的Java字符串,你需要查看读写该格式的方法的规范:
  • 如果使用DataOutput.writeUTF8写入了字符串,则DataInput.readUTF8() javadoc是明确的规范。除了对NUL的非标准处理外,它还指定字符串以无符号16位字节计数开头。

  • 如果使用ObjectOutputStream.writeObject()写入字符串,则序列化规范是明确的。


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