jjzjj

ByteBuffer中文乱码的治疗过程

不知不怪 2023-03-28 原文

学习NIO过程中都会讲一个ByteBuffer读写文件的例子,可是我把文件内容换成中文就不OK了

1.源码

public class FileChannelDemo1 {
    // FileChannel读取数据到buffer中
    public static void main(String[] args) throws Exception {
        // 创建FileChannel
        RandomAccessFile aFile = new RandomAccessFile("d:/中文文本.txt", "rw");
        FileChannel channel = aFile.getChannel();
        // 创建Buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(21);
        // 读取数据到buffer中
        int bytesRead = channel.read(byteBuffer);
        while (bytesRead != -1) {
//          System.out.print("读取了:" + bytesRead);
            byteBuffer.flip();
            while (byteBuffer.hasRemaining()) {
                System.out.print(StandardCharsets.UTF_8.decode(byteBuffer));
            }
            byteBuffer.compact();
            bytesRead = channel.read(byteBuffer);
        }
        aFile.close();
    }
}
/*
d:/中文文本.txt

中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
 */

2.输出
image.png

问题很清楚:中文是由多字节组成,而ByteBuffer按字节处理数据,余下部分不能组成完整的字符,要和下一组数据合并处理才能组成完整字符。
于是百度之
https://blog.csdn.net/hello_junz/article/details/87706732

public class FileChannelTest {
 
    public static void main(String[] args) throws IOException {
        FileChannelTest fcChannelTest = new FileChannelTest();
        fcChannelTest.readFile();
    }
 
    public void readFile() throws IOException {
        // 文件编码是utf8,需要用utf8解码
        Charset charset = Charset.forName("utf-8");
        CharsetDecoder decoder = charset.newDecoder();
 
        File file = new File(getClass().getResource("").getPath(), "中文测试.txt");
        RandomAccessFile raFile = new RandomAccessFile(file, "rw");
        FileChannel fChannel = raFile.getChannel();
 
        ByteBuffer bBuf = ByteBuffer.allocate(32); // 缓存大小设置为32个字节。仅仅是测试用。
        CharBuffer cBuf = CharBuffer.allocate(32);
 
        int bytesRead = fChannel.read(bBuf); // 从文件通道读取字节到buffer.
        char[] tmp = null; // 临时存放转码后的字符
        byte[] remainByte = null;// 存放decode操作后未处理完的字节。decode仅仅转码尽可能多的字节,此次转码不了的字节需要缓存,下次再转
        int leftNum = 0; // 未转码的字节数
        while (bytesRead != -1) {
 
            bBuf.flip(); // 切换buffer从写模式到读模式
            decoder.decode(bBuf, cBuf, true); // 以utf8编码转换ByteBuffer到CharBuffer
            cBuf.flip(); // 切换buffer从写模式到读模式
            remainByte = null;
            leftNum = bBuf.limit() - bBuf.position();
            if (leftNum > 0) { // 记录未转换完的字节
                remainByte = new byte[leftNum];
                bBuf.get(remainByte, 0, leftNum);
            }
 
            // 输出已转换的字符
            tmp = new char[cBuf.length()];
            while (cBuf.hasRemaining()) {
                cBuf.get(tmp);
                System.out.print(new String(tmp));
            }
 
            bBuf.clear(); // 切换buffer从读模式到写模式
            cBuf.clear(); // 切换buffer从读模式到写模式
            if (remainByte != null) {
                bBuf.put(remainByte); // 将未转换完的字节写入bBuf,与下次读取的byte一起转换
            }
            bytesRead = fChannel.read(bBuf);
        }
        raFile.close();
    }
}

3 改造

以上程序逻辑是正确的,能够输出正确结果,但他是使用硬编码的方式处理剩余字节的,其实ByteBuffer 提供这样的api来处理这种问题,就是compact(),本着大道至简的原则对程序加以改造,还请各位看官多提宝贵意见。

public class FileChannelTest {
    public static void main(String[] args) throws IOException {

        RandomAccessFile raFile = new RandomAccessFile("d:/中文文本.txt", "rw");
        FileChannel fChannel = raFile.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(32);
        CharBuffer charBuffer = CharBuffer.allocate(32);
        CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
        while (fChannel.read(byteBuffer) != -1) {
            byteBuffer.flip();
            decoder.decode(byteBuffer, charBuffer, true);
            charBuffer.flip();
            while (charBuffer.hasRemaining()) {
                System.out.print(charBuffer.get());
            }
            byteBuffer.compact();// 压缩:把未读过的内容保留,已读过的内容清理掉
            charBuffer.clear();
        }

        raFile.close();
    }
}

image.png

4 简化

public class FileChannelDemo5 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile raFile = new RandomAccessFile("d:/中文文本.txt", "rw");
        FileChannel fChannel = raFile.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(32);
        CharBuffer charBuffer = byteBuffer.asCharBuffer();
        for (CharsetDecoder decoder = Charset.defaultCharset().newDecoder(); fChannel.read(byteBuffer) != -1;) {
            byteBuffer.flip();
            decoder.decode(byteBuffer, charBuffer, true);
            charBuffer.flip();
            System.out.print(charBuffer.toString());
            byteBuffer.compact();// 压缩:把未读过的内容保留,已读过的内容清理掉
            charBuffer.clear();
        }
        raFile.close();
    }
}

有关ByteBuffer中文乱码的治疗过程的更多相关文章

  1. 亚特兰蒂斯的回声(中文版): chatGPT 的杰作 - 2

    英文版英文链接关注公众号在“亚特兰蒂斯的回声”中踏上一段难忘的冒险之旅,深入未知的海洋深处。足智多谋的考古学家AriaSeaborne偶然发现了一件古代神器,揭示了一张通往失落之城亚特兰蒂斯的隐藏地图。在她神秘的导师内森·兰登教授的指导和勇敢的冒险家亚历克斯·默瑟的帮助下,阿丽亚开始了一段危险的旅程,以揭开这座传说中城市的真相。他们的冒险之旅带领他们穿越险恶的大海、神秘的岛屿和充满陷阱和谜语的致命迷宫。随着Aria潜在的魔法能力的觉醒,她被睿智勇敢的QueenNeria的幻象所指引,她让她为即将到来的挑战做好准备。三人组揭开亚特兰蒂斯令人惊叹的隐藏文明,并了解到邪恶的巫师马拉卡勋爵试图利用其古

  2. ruby - Ruby 中的 block 和过程 - 2

    我已经开始学习Ruby,我已经阅读了一些教程,甚至还买了一本书(“ProgrammingRuby1.9-ThePragmaticProgrammers'Guide”),我遇到了一些以前从未见过的新东西使用我知道的任何其他语言(我是一名PHP网络开发人员)。block和过程。我想我明白它们是什么,但我不明白的是为什么它们如此伟大,以及我应该在何时何地使用它们。我到处都看到他们说block和过程是Ruby中的一个很棒的特性,但我不理解它们。这里有人能给像我这样的Ruby新手一些解释吗? 最佳答案 block有很多好处。电梯演讲:bloc

  3. ruby-on-rails - Rails 中 View 的解析过程 - 2

    rails中View的解析过程是怎样的?我对View中erb标记中原始html与ruby​​代码的解析顺序部分感兴趣。我认为这是View代码被解析并最终发送给请求者的顺序:Controller调用ViewView代码从上到下解析当Rails在解析过程中遇到erb标记时:rails解析它并将结果附加到解析的html(这包括erb标签引用助手)一旦整个View被解析,整体结果将发送给请求者这似乎并非如此。看来View代码会扫描任何erb片段并首先解析那些片段(包括对助手的引用)。之后,rails然后从上到下解析所有View代码并将结果发送给请求者。以这个View为例:#_form.html

  4. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理) - 2

    快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言  目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u

  5. 「想体验ChatGPT中文聊天?」那快进来,你用不上算我输 - 2

    ♥️作者:白日参商🤵‍♂️个人主页:白日参商主页♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!「想体验ChatGPT中文聊天?」那快进来,你用不上算我输项目场景:项目条件一、那就开始吧1、安装ChatGPT-Desktop2、OpenAPI设置二、使用实例恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!项目场景:近几个月可以说ChatGPT是火得一

  6. 计算机网络笔记:TCP三次握手和四次挥手过程 - 2

    TCP是面向连接的协议,连接的建立和释放是每一次面向连接的通信中必不可少的过程。TCP连接的管理就是使连接的建立和释放都能正常地进行。三次握手TCP连接的建立—三次握手建立TCP连接①若主机A中运行了一个客户进程,当它需要主机B的服务时,就发起TCP连接请求,并在所发送的分段中用SYN=1表示连接请求,并产生一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x。主机B收到A的连接请求报文,就完成了第一次握手。客户端发送SYN=1表示连接请求客户端发送一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x②主机B如果同意建立连接,则向主机A发送确认报

  7. 对于体育新闻中文文本关键字提取有哪些关键字提取算法及其步骤 - 2

    对于体育新闻中文文本的关键字提取,常用的算法包括TF-IDF、TextRank和LDA等。它们的基本步骤如下:1.TF-IDF算法: -将文本进行分词和词性标注处理。-统计每个词在文本中的词频(TF)。-计算每个词在整个语料库中出现的文档频率(DF)和逆文档频率(IDF)。-计算每个词的TF-IDF值,并按照值的大小进行排序,选择排名前几的词作为关键字。2.TextRank算法:-将文本进行分词和词性标注处理。-将分词结果转化成图模型,每个词语为节点,根据词语之间的共现关系建立边。-对图模型进行迭代计算,计算每个节点的PageRank值,表示该节点的重要性。-选择排名前几的节点作为关键字。3.

  8. 关于如何为 PostgreSQL 编写存储过程的 Ruby 教程? - 2

    听说PostgreSQL的可以用Ruby写存储过程但我一直没能找到更多关于它的信息,教人们如何实际去做。有人可以为此推荐好的资源。谢谢 最佳答案 显然,您需要安装PL/Ruby。之后,你可以写:CREATEFUNCTIONruby_max(int4,int4)RETURNSint4AS'ifargs[0].to_i>args[1].to_ireturnargs[0]elsereturnargs[1]end'LANGUAGE'plruby';查看其GitHubrepository安装说明。

  9. ruby - ruby 中的过程和数据抽象 - 2

    我是Ruby新手。我正在学习ruby​​中的抽象原则。据我了解,过程抽象是对用户隐藏实现细节,或者只是专注于要点而忽略细节。我关心的是如何实现它1)是不是一个简单的函数调用就这样#functiontosortarray#@paramsarray[Array]tobesortdefmy_sort(array)returnarrayifarray.sizearray[i+1]array[i],array[i+1]=array[i+1],array[i]swapped=trueendendendarrayend然后这样调用sorted_array=my_sort([12,34,123,43,

  10. ruby - 如何将 Interactive Ruby 整合到我的开发过程中? - 2

    我正在尝试找到一种更好的方法将IRB与我的常规ruby​​开发集成。目前我很少在我的代码中使用IRB。我只用它来验证语法或尝试一些小的东西。我知道我可以将我自己的代码加载到ruby​​中作为一个require'mycode'但这通常不符合我的编程风格。有时我要检查的变量超出范围或在循环内。有没有一种简单的方法可以启动我的脚本并在IRB内的某个点卡住?我想我正在寻找一种更简单的方法来调试我的ruby​​代码而不破坏我的F5(编译)键。也许有经验的ruby开发者可以和我分享一个更精简的开发方法。 最佳答案 安装ruby​​-debugg

随机推荐