RandomAccessFile 乱码问题

目录

  • RandomAccessFile 乱码问题
  • 分析
    • byte数组读取
    • byte数组截断
    • 大byte直接转换
  • readLine乱码深度解析
    • 原值转换
  • 总结
  • 最佳实践

RandomAccessFile 乱码问题

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数组读取

直接读取到自定义的缓冲区 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你好

现在看起来,简直美滋滋,终于读取到了中文内容了.

那么这样读取的时候,缓冲区将中文截断时还是一样的吗?

byte数组截断

@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  �) � � � � �

当中文被截断后,之后的内容也跟着变成乱码。

大byte直接转换

   @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乱码都是同理

readLine乱码深度解析

直接分析源码:

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数组截断问题。

你可能感兴趣的:(热心回答,java核心)