资讯专栏INFORMATION COLUMN

Java NIO下使用ByteBuffer读取文本时解决UTF-8概率性中文乱码的问题

SolomonXie / 3656人阅读

摘要:场景读取一个大文本文件,并输出到控制台。示例代码如下至少为,因为最大为字节,阻止读取跨界数据恢复

场景:
读取一个大文本文件,并输出到控制台。

在这里我们选择使用nio进行读取文本文件,在输出的过程中,有些文件中英文都显示正常,有些则偶尔出现中文乱码,经思考发现,在 ByteBuffer.allocate 时分配空间,如果中英混合的文件中就会出现中文字符只读取了一部分的问题,如果文本为等长编码字符集的时候,可以根据编码集 byte 长度进行 allocate ,例如 GBK 为2 byte ,所以我们 allocate 时未2的倍数即可,但像 UTF-8 这类变长的编码字符集时则没那么简单了。

下面就是 UTF-8 的编码方式

0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

对于 UTF-8 编码中的任意字节 B ,如果 B 的第一位为0,则 B 为 ASCII 码,并且 B 独立的表示一个字符;

如果 B 的第一位为1,第二位为0,则B为一个非 ASCII 字符(该字符由多个字节表示)中的一个字节,并且不是字符的第一个字节编码;

如果 B 的前两位为1,第三位为0,则B为一个非 ASCII 字符(该字符由多个字节表示)中的第一个字节,并且该字符由两个字节表示;

如果 B 的前三位为1,第四位为0,则B为一个非 ASCII 字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示;

如果 B 的前四位为1,第五位为0,则B为一个非 ASCII 字符(该字符由多个字节表示)中的第一个字节,并且该字符由四个字节表示;

通过分析我们发现,在读取中我们通过处理临界值来解决 UTF-8 编码字符读取问题。

示例代码如下:
RandomAccessFile rf = new RandomAccessFile("zh.txt", "rw");
FileChannel channel = rf.getChannel();

ByteBuffer buffer = ByteBuffer.allocate(4); // 至少为4,因为UTF-8最大为4字节

while (channel.read(buffer) != -1) {

    byte b;
    int idx;
    out :
    for (idx = buffer.position()-1; idx >= 0; idx--) {
        b = buffer.get(idx);

        if ((b & 0xff) >> 7 == 0) {  // 0xxxxxxx
            break;
        }
        if ((b& 0xff & 0xc0) == 0xc0) {   // 11xxxxxx,110xxxxx、1110xxxx、11110xxx
            idx -= 1;
            break;
        }
        if ((b & 0xff & 0x80) == 0x80) {
            for (int i = 1; i < 4; i++) {
                b = buffer.get(idx - i);
                if ((b & 0xff & 0xc0) == 0xc0) {
                    if ((b & 0xff) >> (5 + 1 - i) == 0xf >> (3 - i)) {
                        break out;
                    } else {
                        idx = idx - 1 - i;
                        break out;
                    }
                }
            }
        }
    }


    buffer.flip();
    int limit = buffer.limit();
    buffer.limit(idx+1);  // 阻止读取跨界数据
    System.out.println(Charset.forName("UTF-8").decode(buffer).toString());

    buffer.limit(limit);  // 恢复limit
    buffer.compact();
}

channel.close();
rf.close();

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76787.html

相关文章

  • 一文理解:Java NIO 核心组件

    摘要:的出现解决了这尴尬的问题,非阻塞模式下,通过,我们的线程只为已就绪的通道工作,不用盲目的重试了。注意要将注册到,首先需要将设置为非阻塞模式,否则会抛异常。 showImg(https://segmentfault.com/img/remote/1460000017053374); 背景知识 同步、异步、阻塞、非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下。 ...

    Coding01 评论0 收藏0
  • 一文让你彻底理解 Java NIO 核心组件

    摘要:的出现解决了这尴尬的问题,非阻塞模式下,通过,我们的线程只为已就绪的通道工作,不用盲目的重试了。注意要将注册到,首先需要将设置为非阻塞模式,否则会抛异常。 同步、异步、阻塞、非阻塞首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下[1]。 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节)。 异步:相对于同步,API调用返回时调用者不知道操作...

    guyan0319 评论0 收藏0
  • 深入分析 Java Web 中中文编码问题

    摘要:文章首发地址深入分析中的中文编码问题背景编码问题一直困扰着程序开发人员,尤其是在中更加明显,因为是跨平台的语言,在不同平台的编码之间的切换较多。 文章首发地址:深入分析 Java Web 中的中文编码问题 背景: 编码问题一直困扰着程序开发人员,尤其是在 Java 中更加明显,因为 Java 是跨平台的语言,在不同平台的编码之间的切换较多。接下来将介绍 Java 编码问题出现的根本原...

    jsyzchen 评论0 收藏0
  • 少啰嗦!一分钟带你读懂JavaNIO和经典IO区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0
  • 如何用Java与python代码解释IO模型

    摘要:缓冲区的限制不能为负,并且不能大于其容量。如果指向的位置超过限制,则抛出异常。使用临时缓冲区执行低层次操作。临时缓冲区对象离开作用域,并最终成为被回收的无用数据。 前天刚好看了点《UNIX网络编程》,比较头大。现在我来整理一下所学所得,并用于个人备忘。如果有不对,请批评。 想要解锁更多新姿势?请访问https://blog.tengshe789.tech/ IO模型介绍 IO模型是什么...

    SnaiLiu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<