最近在修改一个使用Window 给FTP服务器上传中文路径的问题,头疼了很久。
从Windows读取到的字符在遇到中文个数为奇数的时候,路径会被部分识别为乱码。而中文个数为偶数个,则能正常识别出来。
以下是在网上看到某位大牛分析的原因,看了才知道,原来是java输出流搞的鬼。
引用:
最近被utf-8搞得各种头痛。差点就要练出肉眼看二进制编码的火眼金睛。先举个今天遇到的小问题吧。
java用gbk方式写入的文件,似乎有个特性,是会自动把gbk编码中不能识别的0×0这个字节,替换成”?”,也即0x3F。这原本也没什么,但当他读取utf8文本并重新写入新的文件的时候,就会出现偏差。
众所周知,utf8编码的英文和符号部分是同ASCII编码兼容的。同时,在大多数中文中,很少出现0×0这样的字节。因此,用ASCII硬写utf8编码的中英混合文字,然后这个文件再用utf8的方式打开,运气好的话居然可以侥幸蒙混过关,毫无错误。然而,一旦文字中出现了带有0×0字节的utf8编码,就没那么侥幸了。
例如“个性”的性字,其unicode编码为0×6027, 翻译成utf8的话:
性
60 27
01100000 00100111
[1110]0110 [10]000000 [10]100111
E6 80 A7
这里用方括号标识了utf8的字节头,方便辨识。在windows上是使用小端序,所以实际的序列会是这样:
0110 1110 0000 1000 0111 1010
这里第三个字节出现了0×0,悲剧发生了,被java的流输出不知怎么就那么智能替换成了”?”
0110 1110 1111 0011 ...
6 E F 3
结果这里就乱码了。打出来内容一团糟。变成“个<E6>?”。
另外一个曾让我头疼一天的问题,就是根据utf8编码规则,并不会出现0xC0 0×80这个双字。因为如果按照编码规范
0xC0 0x80
[110]0 0000 [10]00 0000
0000 0000
这根本就是0×0嘛!不可能编码成这个样子的。wiki上查了半天才发现,原来这个又是windows的专有解决方案。完全是windows系统一厢情愿,想要区别于一般的ASCII文本而作的别扭事。结果这样的文本想要插入到mysql数据库时,就会被数据库认为是不合理的utf8编码而被冷冷拒绝!
所以整理utf8的编码问题,有时候还是要一直看到二进制上面来,这样问题发生在哪里也就会比较清楚,该怎样解决就不再像是霰弹枪编程,左试右试都不知道哪里出错了哟。