Java NIO ByteBuffer读取UTF-8文件截断Unicode字符如何解决
问题根因
- UTF-8是变长编码,常见中文字符每个占3字节,当前实现每次读取满缓冲区后直接将所有字节解码为字符,当缓冲区末尾剩余的字节不足以组成一个完整的UTF-8字符时,就会产生乱码。该问题和缓冲区大小没有本质关系,只要缓冲区不能一次性装下整个文件的所有内容,就有概率出现截断问题。
- 原代码中空吞IOException的写法不推荐,出现文件不存在、无读取权限等问题时会直接返回空字符串,难以排查问题。
解决方案1:使用CharsetDecoder处理断码场景(保留FileChannel实现)
核心思路是复用CharsetDecoder保留未完成解码的字节状态,同时用compact()替代clear(),将未解码的剩余字节保留在缓冲区中,下一轮读取新字节后再一起解码,即使缓冲区只分配10字节也不会出现乱码,内存占用稳定,支持超大文件读取:
public static String ReadFromFile(String fileLocation) throws IOException { StringBuilder result = new StringBuilder(); // 初始化UTF-8解码器 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); // 用try-with-resources自动关闭资源,无需手动写finally逻辑 try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileLocation, "r"); FileChannel fileChannel = randomAccessFile.getChannel()) { ByteBuffer byteBuffer = ByteBuffer.allocate(10); CharBuffer charBuffer = CharBuffer.allocate(10); int bytesRead = fileChannel.read(byteBuffer); while (bytesRead != -1) { byteBuffer.flip(); // 解码时自动保留不完整字符的状态,等待下一轮读取后再处理 decoder.decode(byteBuffer, charBuffer, bytesRead == -1); charBuffer.flip(); result.append(charBuffer); charBuffer.clear(); // 保留未解码的剩余字节到下一轮 byteBuffer.compact(); bytesRead = fileChannel.read(byteBuffer); } // 处理最后剩余的字符 decoder.flush(charBuffer); charBuffer.flip(); result.append(charBuffer); } return result.toString(); }
解决方案2:用Java内置工具类简化实现
如果没有强制使用RandomAccessFile的需求,直接用JDK7及以上提供的NIO.2工具类即可,代码更简洁且不易出错:
小文件场景直接读取全量内容
// 一行代码完成读取,自动处理编码问题 String content = Files.readString(Paths.get("test.txt"), StandardCharsets.UTF_8);
大文件流式读取
try (BufferedReader reader = Files.newBufferedReader(Paths.get("test.txt"), StandardCharsets.UTF_8)) { String line; StringBuilder result = new StringBuilder(); while ((line = reader.readLine()) != null) { result.append(line).append("\n"); } }
内容的提问来源于stack exchange,提问作者Paper_Folding
相关产品推荐
相关产品推荐

