中文编码笔记

[size=large][b]问题描述:[/b][/size]

上周在压缩tar格式文件的时候, 遇到了乱码问题。
既是文件名在tar文件中显示为乱码。 如下图所示

[img]http://dl2.iteye.com/upload/attachment/0094/7305/a8b801e7-f718-3825-a0bc-cf744dc4936d.jpg[/img]

[b]你好[/b]被转换成了[b]浣犲ソ[/b]

为什么会这样, 首先要了解一下中文编码。

[b]国标码和unicode[/b]
[i]国家标准强制标准冠以“GB”。现时中华人民共和国官方强制使用GB 18030标准,但较旧的计算机仍然使用GB 2312。[/i]
正如chrome浏览器里面所显示的三种中文编码-> utf-8, gbk, gb18030.
个人感觉一般开发都使用utf8和gbk编码。
而 "你好"->"浣犲ソ" 在这里也正是使用gbk标准来解码utf8编码文字所产生的错误。


[b]GBK和Unicode的编码表[/b]
utf8:[url]http://blog.csdn.net/qiaqia609/article/details/8069678[/url]
gbk: [url]http://ff.163.com/newflyff/gbk-list/[/url]

这里就查看“你”这个汉字是怎么编码的。 在GBK中。对应的是C4 E3,而在utf-8中,对应的是E4 BD A0。
(注:最初我以为gbk的编码和utf8的编码之间会有一定的联系,一直在找它们的规律。结果发现, 我完全想错了。 他们的编码是没有什么可以转换的关系的。后面再说)

[size=large][b]编码解码过程[/b][/size]
上述编码全部都是十六进制的数字。 这里说一下编码解码的过程,拿
String str="你" 为例,先看一下文字是如何转换为二进制的。
GBK:
String gbk="你";

byte[] bytes=gbk.getBytes("gbk");

System.out.println(bytes.length);

for(int i=0;i<bytes.length;i++){
System.out.println(bytes[i]);
int code= bytes[i]+256;
System.out.println(code);//C4 E3
}

这里的重点是方法 getBytes(Charset charset)。
该方法说明如下:
[color=gray]Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array. [/color]
所以byte[] bytes=gbk.getBytes("gbk")就将“你”这个汉字根据gbk编码标准,转换成了二进制的byte对象,放入byte[] bytes中。

后面的Integer.toHexString(code).toUpperCase()就会输出两个二进制字节所对应的十六位数字, 也就是C4 E3, 既GBK编码标准表中的“你”的区域位置。


那么软件又是如何解码的呢?
假如在上面的代码后面加入:
String gbk2=new String(bytes);
System.out.println(gbk2);

运行程序,就会看到这里输出的是 ��。
原因就是,程序在编码的时候用的是gbk标准,产生了16位长度的二进制数字。
而我们的class文件的格式是utf-8的, 当使用new String(bytes)时,程序会[b]默认使用utf8[/b]标准来进行解码。 这样自然不能正确的解码gbk格式的二进制数字。

就好比我们用realplayer去播放mkv格式的视频,是无法播放的, 因为realplayer不知道如何去解码mkv格式。

解决办法很简单,查看String的构造函数,就能看到
[color=violet]java.lang.String.String(byte[] bytes, String charsetName) throws UnsupportedEncodingException

Constructs a new String by decoding the specified array of bytes using the specified charset. The length of the new String is a function of the charset, and hence may not be equal to the length of the byte array. [/color]

通过使用特定编码来解码bytes数组来创建一个String对象。
因此只要使用
String gbk2=new String(bytes,"gbk");
System.out.println(gbk2);

运行程序,就能看到正确的“你”的显示了。
因为程序调用了GBK标准,并以正确的解码方法,既两个字节表示一个汉字,来查找GBK标准表中所对应的汉字,就找到了“你”。


[size=large][b]系统默认编码[/b][/size]
我们的系统默认是使用unicode标准对文件进行编码解码的。
以win7为例, 打开控制面板->区域和语言-》管理, 可以看到:

[img]http://dl2.iteye.com/upload/attachment/0094/7392/f62b4353-42c3-3ad2-b3f1-53f898324a51.jpg[/img]
所谓非Unicode程序所使用的语言, 既是当我们打开一个程序,而该程序所使用的编码不是Unicode, 这时候系统要使用的解码方法。

举个例子, 我们要打开一个日文软件,该日文软件并不是使用的utf8编码,而是其它的日本本地的编码, 这时候系统便会调用我们在这里所设置的[b]非Unicode程序所使用的语言[/b]。 我猜大概就是GBK GB之类的。 于是我们就能看到满屏的乱码。
因此我们如果把这里修改为 日本(日语),重启电脑再打开该日文软件,就会正确显示日文了。 原因也就是系统用正确的编码标准来解码了。


[size=large][b]tar文件名乱码问题[/b][/size]
回到最初遇到的问题。我在网上查了很久, 最后看到
[url]http://www.dewen.org/q/4494/[/url] 这里说[b]tar压缩格式不会记录字符集, 而rar和zip会[/b]。 如果这样的话, 那tar真是不适合中文。

具体来说就是, 文件名被压缩程序设置了utf8编码, 但是到了tar中, tar不支持utf8,于是我们的系统就使用了[b]非Unicode程序所使用的语言[/b]来进行解码。
于是呢, 就有了以下步骤:


[color=red]1. 定义了一个字符“你”, 然后该字符被用'utf8'编码, 变成了2进制11100100,10111101,10100000
之后此二进制被传入了某一个程序中, 该程序不知道‘你’这个字是什么编码格式的, 于是尝试用gbk进行解码。
GBK是怎么解码的呢? 会把这段二进制当做每16位表示一个字符。
于是前面的11100100,10111101就被提取了出来, 然后程序开始在GBK编码表中搜索对应的字符。
结果就找到了 E4 BD->浣 。
接下来的10100000没法解码, 就出来了一个问号'?'。[/color]

这段话是之前做的笔记, 也就是系统使用国标码标准来解码utf-8编码文字所出现的问题。


最后放上tar文件压缩的问题代码。


System.out.println(System.getProperty("file.encoding"));
System.out.println(Charset.defaultCharset().name());


String path="D:\\_thumbnail";
String f="d:\\t.tar";
TarArchiveOutputStream taos=new TarArchiveOutputStream(new FileOutputStream(f),"utf-8");//这里设置为GBK就可以解决乱码问题



File of=new File(path);

File[] ofs=of.listFiles();
System.out.println(ofs.toString());
for(File off:ofs){
String parent=off.getParentFile().getParent(); //D:/
String parent_relative=off.getParentFile().getAbsolutePath().substring(parent.length());

FileInputStream fis=new FileInputStream(off);
System.out.println(File.separator);
TarArchiveEntry tae=new TarArchiveEntry(parent_relative+File.separator+new String(off.getName().getBytes("utf-8")));//这里的参数是压缩文件里的路径,也就是文件夹和文件名
//比如_thumbnail/1.jpg,就会产生一个_thumnbnail的文件夹和一个1.jpg的文件。
tae.setSize(off.length());
taos.putArchiveEntry(tae);

IOUtils.copy(fis, taos);
fis.close();
taos.closeArchiveEntry();
}



TarArchiveOutputStream taos=new TarArchiveOutputStream(new FileOutputStream(f),"utf-8");//这里设置为GBK就可以解决乱码问题

[color=violet]Constructor for TarInputStream.

Parameters:
os the output stream to use
encoding name of the encoding to use for file names[/color]


如注释写的一样, 这里不写编码格式,则会使用默认的utf-8来进行文件名编码。
之所以设置GBK编码不会出现乱码,就是因为系统得不到tar压缩文件名的编码信息,就采用了非Unicode所使用语言,既GBK, 来进行解码。


[size=large][b]解决方法:[/b][/size]
压缩tar文件时,文件名用英文即可。

终于写完了-O- 写的不是一般的乱
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值