如何使用AsynchronousFileChannel高效地将数据读入StringBuffer

5

您知道可以使用AsynchronousFileChannel将整个文件读取为String:

 AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(filePath, StandardOpenOption.READ);
            long len = fileChannel.size();

            ReadAttachment readAttachment = new ReadAttachment();
            readAttachment.byteBuffer = ByteBuffer.allocate((int) len);
            readAttachment.asynchronousChannel = fileChannel;

            CompletionHandler<Integer, ReadAttachment> completionHandler = new CompletionHandler<Integer, ReadAttachment>() {

                @Override
                public void completed(Integer result, ReadAttachment attachment) {

                    String content = new String(attachment.byteBuffer.array());
                    try {
                        attachment.asynchronousChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    completeCallback.accept(content);
                }

                @Override
                public void failed(Throwable exc, ReadAttachment attachment) {
                    exc.printStackTrace();
                    exceptionError(errorCallback, completeCallback, String.format("error while reading file [%s]: %s", path, exc.getMessage()));
                }
            };

            fileChannel.read(
                    readAttachment.byteBuffer,
                    0,
                    readAttachment,
                    completionHandler);

假设现在,我不想分配整个ByteBuffer,而是逐行读取。我可以使用固定宽度的ByteBuffer,并一直调用read,每次都将其复制并附加到StringBuffer中,直到我无法得到新行... 我唯一关心的是:因为我正在读取的文件的编码可能是每个字符多字节(UTF something),所以读取的字节可能以不完整的字符结尾。如何确保我将正确的字节转换为字符串,而不会弄乱编码? 更新:答案在所选答案的评论中,但基本上指向CharsetDecoder

3
不要使用异步 I/O 读取行。这种方法并不适合。您可以使用 BufferedReader.readLine() 每秒读取数百万行。 - user207421
我需要非阻塞操作! - gotch4
那么你为什么要使用异步I/O呢?那不是非阻塞的。它是第三种范式,继阻塞和非阻塞之后。但是你为什么认为不能一开始就使用阻塞I/O呢? - user207421
如果我使用Handler,它应该是非阻塞的,对吗?你建议使用哪种其他范例? - gotch4
2个回答

1
如果你的输入数据有清晰的ASCII分隔符(例如\n),你就不需要担心不完整的字符串,因为这个字符映射到单字节(反之亦然)。所以只需搜索输入中的 '\n' 字节,并将其前面的内容读取并转换为字符串。循环直到没有更多的换行符被找到。然后压缩缓冲区并重复使用它进行下一次读取。如果没有找到新行,则必须分配更大的缓冲区,复制旧缓冲区的内容,然后再调用读取操作。
编辑:如评论中所述,您可以将ByteBuffer即时传递给CharsetDecoder,将其转换为CharBuffer(然后附加到StringBuilder或其他首选解决方案)。

无论如何,我必须将整行作为字节缓冲区存储...让我们暂时忘记我正在处理行...以及我的缓冲区是有限的(行可能非常长)。我们该怎么做? - gotch4
1
你可以使用http://docs.oracle.com/javase/7/docs/api/java/nio/charset/CharsetDecoder.html#decode(java.nio.ByteBuffer,%20java.nio.CharBuffer,%20boolean)来实现即时转换输入。但是,你仍然需要管理缓冲区,因为它可能包含读取之间的剩余字符。 - Zbynek Vyskovsky - kvr000
完美!谢谢,考虑更新答案。 - gotch4

-2

尝试使用Scanner:

    Scanner sc = new Scanner(FileChannel.open(filePath, StandardOpenOption.READ));
   String line = sc.readLine();

FileChannel 是 InterruptibleChannel


再次强调,我不需要可中断性,我需要启动读取并稍后进行回调... - gotch4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接