问题描述:
上周在压缩tar格式文件的时候, 遇到了乱码问题。
既是文件名在tar文件中显示为乱码。 如下图所示
你好被转换成了
浣犲ソ
为什么会这样, 首先要了解一下中文编码。
国标码和unicode
国家标准强制标准冠以“GB”。现时中华人民共和国官方强制使用GB 18030标准,但较旧的计算机仍然使用GB 2312。
正如chrome浏览器里面所显示的三种中文编码-> utf-8, gbk, gb18030.
个人感觉一般开发都使用utf8和gbk编码。
而 "你好"->"浣犲ソ" 在这里也正是使用gbk标准来解码utf8编码文字所产生的错误。
GBK和Unicode的编码表
utf8:
http://blog.csdn.net/qiaqia609/article/details/8069678
gbk:
http://ff.163.com/newflyff/gbk-list/
这里就查看“你”这个汉字是怎么编码的。 在GBK中。对应的是C4 E3,而在utf-8中,对应的是E4 BD A0。
(注:最初我以为gbk的编码和utf8的编码之间会有一定的联系,一直在找它们的规律。结果发现, 我完全想错了。 他们的编码是没有什么可以转换的关系的。后面再说)
编码解码过程
上述编码全部都是十六进制的数字。 这里说一下编码解码的过程,拿
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)。
该方法说明如下:
Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.
所以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)时,程序会
默认使用utf8标准来进行解码。 这样自然不能正确的解码gbk格式的二进制数字。
就好比我们用realplayer去播放mkv格式的视频,是无法播放的, 因为realplayer不知道如何去解码mkv格式。
解决办法很简单,查看String的构造函数,就能看到
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.
通过使用特定编码来解码bytes数组来创建一个String对象。
因此只要使用
String gbk2=new String(bytes,"gbk");
System.out.println(gbk2);
运行程序,就能看到正确的“你”的显示了。
因为程序调用了GBK标准,并以正确的解码方法,既两个字节表示一个汉字,来查找GBK标准表中所对应的汉字,就找到了“你”。
系统默认编码
我们的系统默认是使用unicode标准对文件进行编码解码的。
以win7为例, 打开控制面板->区域和语言-》管理, 可以看到:
所谓非Unicode程序所使用的语言, 既是当我们打开一个程序,而该程序所使用的编码不是Unicode, 这时候系统要使用的解码方法。
举个例子, 我们要打开一个日文软件,该日文软件并不是使用的utf8编码,而是其它的日本本地的编码, 这时候系统便会调用我们在这里所设置的
非Unicode程序所使用的语言。 我猜大概就是GBK GB之类的。 于是我们就能看到满屏的乱码。
因此我们如果把这里修改为 日本(日语),重启电脑再打开该日文软件,就会正确显示日文了。 原因也就是系统用正确的编码标准来解码了。
tar文件名乱码问题
回到最初遇到的问题。我在网上查了很久, 最后看到
http://www.dewen.org/q/4494/ 这里说
tar压缩格式不会记录字符集, 而rar和zip会。 如果这样的话, 那tar真是不适合中文。
具体来说就是, 文件名被压缩程序设置了utf8编码, 但是到了tar中, tar不支持utf8,于是我们的系统就使用了
非Unicode程序所使用的语言来进行解码。
于是呢, 就有了以下步骤:
1. 定义了一个字符“你”, 然后该字符被用'utf8'编码, 变成了2进制11100100,10111101,10100000
之后此二进制被传入了某一个程序中, 该程序不知道‘你’这个字是什么编码格式的, 于是尝试用gbk进行解码。
GBK是怎么解码的呢? 会把这段二进制当做每16位表示一个字符。
于是前面的11100100,10111101就被提取了出来, 然后程序开始在GBK编码表中搜索对应的字符。
结果就找到了 E4 BD->浣 。
接下来的10100000没法解码, 就出来了一个问号'?'。
这段话是之前做的笔记, 也就是系统使用国标码标准来解码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就可以解决乱码问题
Constructor for TarInputStream.
Parameters:
os the output stream to use
encoding name of the encoding to use for file names
如注释写的一样, 这里不写编码格式,则会使用默认的utf-8来进行文件名编码。
之所以设置GBK编码不会出现乱码,就是因为系统得不到tar压缩文件名的编码信息,就采用了非Unicode所使用语言,既GBK, 来进行解码。
解决方法:
压缩tar文件时,文件名用英文即可。
终于写完了-O- 写的不是一般的乱