问题
Java的文件输入流InputStreamReader虽然有getEncode方法,但是实际上这个方法读出来都是UTF-8格式,这是个bug。
因此只有自行判断
有bom的UTF-8
首先有bom的UTF-8可以通过文件的前3个byte来确定文件的编码,比较简单:
先读取文件前面的byte
inputStream = new FileInputStream(file);
byte[] head = new byte[headSize];
inputStream.read(head);
if (head[0] == -1 && head[1] == -2) {
code = "UTF-16";
System.err.println("文件编码错误: " + file.getName() + " : " + code);
} else if (head[0] == -2 && head[1] == -1) {
code = "Unicode";
System.err.println("文件编码错误: " + file.getName() + " : " + code);
} else if (head[0] == -17 && head[1] == -69 && head[2] == -65) {
code = "UTF-8";
} else {
// 检查无Bom的UTF-8
}
但是实际上我们大多文件是无bom的UTF-8,也就是文件头部没有文件标志的字节,第一个字节就是数据内容。
无bom的UTF-8
网上有很多根据UTF-8的编码规则来判断格式的,可是用于java的都不是很好用。
首先,UTF-8的编码方式:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
实际上都在3字节内,没有遇到4字节以上编码的。根据其编码格式我们就可以文件进行一定的验证了,例如:
{
int i = 0;
code = "UTF-8 NoBom";
while (i < headSize - 2) {
if ((head[i] & 0x00FF) < 0x80) {// (10000000)值小于0x80的为ASCII字符
i++;
continue;
} else if ((head[i] & 0x00FF) < 0xC0) {// (11000000)值在0x80和0xC0之间的,不是开头第一个
code = "Not UTF-8";
System.err.println("文件编码错误: " + file.getName() + " : " + code + "1000");
break;
} else if ((head[i] & 0x00FF) < 0xE0) {// (11100000)此范围内为2字节UTF-8字符
if ((head[i + 1] & (0xC0)) != 0x8) {
code = "Not UTF-8";
System.err.println("文件编码错误: " + file.getName() + " : " + code + "1100");
break;
} else
i += 2;
} else if ((head[i] & 0x00FF) < 0xF0) {// (11110000)此范围内为3字节UTF-8字符
if ((head[i + 1] & (0xC0)) != 0x80 || (head[i + 2] & (0xC0)) != 0x80) {
code = "Not UTF-8";
System.err.println("文件编码错误: " + file.getName() + " : " + code + "11100000" + (head[i + 1] & (0xC0)));
break;
} else
i += 3;
} else {
code = "Not UTF-8";
System.err.println("文件编码错误: " + file.getName() + " : " + code + "1111");
break;
}
}
}
然后用这一段替代前一段代码中 // 检查无Bom的UTF-8
的位置.
这种方法还是存在巧合误判的情况,但是headline设置的越大就越不容易出现错误。
另外,由于UTF-8对ASCII字符的编码与ASCII一致,因此如果所有字符都是ASCII,就无法判断是ASCII还是UTF-8无bom格式,使用本文方法会判断为UTF-8 .