首先,如果你明确的知道使用的编码,那么你可以在读取完毕生成字符串的时候直接指定编码。
例如:
FileInputStream in = new FileInputStream("aaa.txt");
byte[] data = new byte[1024 * 4];
StringBuilder sb = new StringBuilder();
while(in.read(data) > 0) {
sb.append(new String(data,"utf8"); // 指定编码格式,避免乱码}
System.out.println(sb.toString());
这里FileInputStream就是很标准的字节流了,只需要在最终形成字符串的时候指定正确的编码,那么就可以避免乱码。
或者利用新的NIO的API读取:
Path path = Paths.get("D:/aaa.txt");
byte[] data = Files.readAllBytes(path);
String result = new String(data,"utf8"); // 指定编码,可以避免乱码。System.out.println(result);
那么,当我们不确定他的编码的时候,该怎么办呢,这就需要使用类库去猜解他的编码,我用的是这个:cpdetector,他的maven依赖如下:
net.sourceforge.cpdetector
cpdetector
1.0.7
具体用法是这样的:
/*** 初始化一个编码判别器,用于判断文件编码。*/
public static CodepageDetectorProxy getCodePageDetector() {
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
// 这个用于处理文本文件或者有明确编码的文件 detector.add(new ParsingDetector(false));
// 用于检测Unicode的编码 detector.add(UnicodeDetector.getInstance());
// 用于检测各类型的编码 detector.add(JChardetFacade.getInstance());
// 用于检测ASCII类型编码 detector.add(ASCIIDetector.getInstance());
return detector;
}
然后通过这种做法获取具体的编码:
/*** 获取输入流的编码格式,但是似乎这种方法有点问题。*/
public static Charset getCharset(InputStream in) {
CodepageDetectorProxy detectorProxy = getCodePageDetector();
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(in)) {
return detectorProxy.detectCodepage(bufferedInputStream, 128);
} catch (Exception e) {
e.printStackTrace();
return Charset.defaultCharset();
}
}
/*** 获取文件的编码格式,一般用到的就是他了。*/
public static Charset getCharset(File file) {
CodepageDetectorProxy detectorProxy = getCodePageDetector();
try {
return detectorProxy.detectCodepage(file.toURI().toURL());
} catch (Exception e) {
return Charset.defaultCharset();
}
}
那么经过上面两个步骤,我们就能够得到一个charset对象,用他创建string,如果猜测的编码是正确的,就不会出现乱码,当然猜测代码比较耗费时间的,这个在使用的时候要注意一下。
说到乱码,我们得知道输入流是怎么处理字符的。
InputStream是一个读取字节的东西,因为无论是什么文本数据,存到系统的时候都是二进制的字节,为了能正确显示字符,需要有一个东西把字符表示为二进制字节,这个东西就是编码表。
不同的码表的字符和他对应的二进制字节的对应关系是不一致的,那么如果一个字符通过编码格式A编码,但是读取的时候通过编码格式B读取,那么就有一定几率出现乱码,为什么是有一定几率呢,是因为字符集有的是可以相互兼容的,尤其是英文字符部分,不兼容的部分就会出现乱码。
那么java是怎么处理字符流的呢?
Java的字符流从本质上来说,是字节流的衍生,他通过StreamDecoder和StreamEncoder进行处理,StreamEncoder的作用是将字符按照某种编码输出为字节,StreamDecoder作用是将某些字节按照某个编码方式解码成字符,你可以自己指定使用的编码类型,当然你不指定,他也会有一个默认的编码类型,而一旦你的文件编码和默认编码不一致,而你又没有指定字符集,那么出现乱码几乎是必然的。
因此我不赞同你认为字符流能够解决乱码问题,在这个问题上没有一劳永逸的办法,如果阁下知道更合理的解决方案欢迎补充,或者指出回答中不正确的部分。