目录
d:/123.txt 的内容(无BOM UTF-8格式编码)
ea你好
测试代码
@Test
public void testReadLine() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
String content = randomAccessFile.readLine();
System.out.println(content);
}
控制台输出
eaä½ å¥½
这里可以看到,读取到的内容是一串乱码. 这里按照一般的逻辑忍不住对读取到的content进行编码转换
@Test
public void testReadLineRepair() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
String content = randomAccessFile.readLine();
//类似这样
//content = new String(content.getBytes(),"GB2312");
//或者类似这样
//content = new String(content.getBytes(),"UTF-8");
System.out.println(content);
}
然而不管我们怎么去转换都无法获得正确的字符串数值。
这里采用渐进式分析
要了解乱码,首先就要了解编码的一个基本知识
英文字母和中文汉字在不同字符集编码下的字节数
英文字母:
字节数 : 1;编码:GB2312
字节数 : 1;编码:UTF-8
中文汉字:
字节数 : 2;编码:GB2312
字节数 : 3;编码:UTF-8
可以看出来,文件123.txt
的内容以UTF-8编码时,占用了1+1+3+3=8个字节
那么我们尝试另一种方式,直接读取byte转换
直接读取到自定义的缓冲区 byte[]
里面.
@Test
public void testReadBytes() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
byte [] buffer = new byte[1024];
randomAccessFile.read(buffer);
System.out.println(new String(buffer));
}
控制台输出
ea你好
现在看起来,简直美滋滋,终于读取到了中文内容了.
那么这样读取的时候,缓冲区将中文截断时还是一样的吗?
@Test
public void testReadBytesCut() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
byte [] buffer = new byte[3];
int realLength = 0;
StringBuilder builder = new StringBuilder();
//randomAccessFile.read 返回实际读取到的字节数
while((realLength=randomAccessFile.read(buffer))>0){
builder.append(new String(buffer,0,realLength));
}
System.out.println(builder);
}
控制台输出
ea������
这里发现出现了新的乱码,那是因为截断了中文的长度。看下面的例子
正常读取:
65 61 (E4 BD A0) (E5 A5 BD)
e a ( 你 )( 好 )
截断读取:
(65 61 E4) BD A0 E5 A5 BD
(e a �) � � � � �
当中文被截断后,之后的内容也跟着变成乱码。
@Test
public void testReadBytesBigBytes() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte [] buffer = new byte[3];
int realLength = 0;
while((realLength=randomAccessFile.read(buffer))>0){
byteArrayOutputStream.write(buffer,0,realLength);
}
System.out.println(new String(byteArrayOutputStream.toByteArray()));
}
控制台输出
ea你好
这样就已经解决了,当缓存不足时,中文被截断的问题.
在读取HTTP,本地文件时,使用缓冲方式转换成UTF-8乱码都是同理
直接分析源码:
public final String readLine() throws IOException {
StringBuffer input = new StringBuffer();
int c = -1;
boolean eol = false;
while (!eol) {
switch (c = read()) {
case -1:
case '\n':
eol = true;
break;
case '\r':
eol = true;
long cur = getFilePointer();
if ((read()) != '\n') {
seek(cur);
}
break;
default:
// 重点就这一句
input.append((char)c);
break;
}
}
if ((c == -1) && (input.length() == 0)) {
return null;
}
return input.toString();
}
重点就在于,把int类型转换成了char类型,然后在添加到StringBuffer。
此时只要将readLine的的结果从char[]转成byte[],然后在new String就可以解决乱码问题了
// 将 char[] 强转为 byte[]
public static byte[] getBytes(char[] chars) {
byte [] result = new byte[chars.length];
for(int i=0;ibyte) chars[i];
}
return result;
}
@Test
public void testDepthReadLine() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("d:/123.txt","r");
String content = randomAccessFile.readLine();
// toCharArray 是文件实际上读取到的int类型的值,强转为char的
// getBytes() 是经过编码后的Ascii 字符的字节。
// 这里解释一下 两者之间的区别
byte[] bytes = getBytes(content.toCharArray());
System.out.println(new String(bytes));
}
本文介绍了两种解决方式
1. 大byte转换
2. 原值转换
RandomAccessFile 是字节流,只能按字节读取
关于 RandomAccessFile 的其他特性,这里不进行一一说明
使用apache的commons-io读取文件
maven:
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
写法
@Test
public void testCommonsIoUtils() throws IOException {
String content = FileUtils.readFileToString(new File("d:/123.txt"), "UTF-8");
System.out.println(content);
}
这里使用的是FileInputStream字符流,所以不存在 byte数组截断
问题。