概述
java读写文件的有很多种方式,基本都是采用java.io的inputStream和各种基于inputstream的封装实现对文件的读写,最原始的接口提供的便是基于byte的读写,而String可以看做是char[],一个char是8个byte。在最原始的ASCII编码中,我们采用一个字节 也就时8位来表示一个字符(图形字符或者控制字符),而后来1个字节不足以表示现实中的所有字符,于是出现了各种各样的编码格式,常见的比如UTF-8,GBK,UNICODE等。java中的string也是遵循jre中定义的默认字符集(基本为UTF-8),而在byte[]转化成String的过程中可能会由于编码字符集问题导致String逆向回来的byte[]与原来的数组不一致。
问题描述
使用Java 读取一个zip文件,转化成String从客户端传输到服务器,客户端保存到本地文件中,发现客户端得到zip文件不能使用,提示文件损坏或者结尾损坏。比较源文件和生成文件的字符,发现字符不一致。
测试程序
public class Test_Keyczar {
public static void main(String[] args) {
try {
byte[] out = readFileInBytesToString("D:\\下载\\a+b.zip");
File outfile = new File("D:\\下载\\a-b1221.zip");
if (!outfile.exists()) {
outfile.createNewFile();
}
DataOutputStream fw = new DataOutputStream(new FileOutputStream(
outfile));
fw.write(new String(out).getBytes());
fw.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static byte[] readFileInBytesToString(String filePath) {
final int readArraySizePerRead = 4096;
File file = new File(filePath);
ArrayList<Byte> bytes = new ArrayList<>();
try {
if (file.exists()) {
DataInputStream isr = new DataInputStream(new FileInputStream(
file));
byte[] tempchars = new byte[readArraySizePerRead];
int charsReadCount = 0;
while ((charsReadCount = isr.read(tempchars)) != -1) {
for(int i = 0 ; i < charsReadCount ; i++){
bytes.add (tempchars[i]);
}
}
isr.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return toPrimitives(bytes.toArray(new Byte[0]));
}
static byte[] toPrimitives(Byte[] oBytes) {
byte[] bytes = new byte[oBytes.length];
for (int i = 0; i < oBytes.length; i++) {
bytes[i] = oBytes[i];
}
return bytes;
}
}
测试结果
- 如果以byte[]读入文件 不加修改直接写入文件,源文件和生成文件一致
- 如果以byte[]读入文件 采用new String(byte[])的方式转化成String,之后在用String.getBytes()的方式转化为byte[]写文件,源文件和生成文件字符不一致
问题原因分析
java的默认字符集为UTF-8,所以在new String(byte[])的时候发生了字符集的转化,在原始的二进制文件中,字符是按照ascii的形式组织的,而utf-8采用的是一种可变长的编码,对于原始的ascii的数据的解码会导致字符数组的改变,而在String.getBytes()也是得到经过UTF-8编码过后的字符数组。
解决方案
为了排除字符集的问题,可以采用以下两种方案
- 以byte数组的方式读取和处理字符数组,不加转化
- 在byte[]转化成String的时候指定字符集为 ISO-8859-1,在String再转化成byte[]的时候也指定字符集为ISO-8859-1
参考
http://fantaxy025025.iteye.com/blog/1899635
http://www.cnblogs.com/kenkofox/archive/2010/04/23/1719009.html