首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AsynchronousFileChannel如何读取大文件?

AsynchronousFileChannel如何读取大文件?
EN

Stack Overflow用户
提问于 2013-10-23 03:00:58
回答 4查看 6K关注 0票数 5
代码语言:javascript
复制
  Path file = Paths.get("c:/large.log");
  AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
  final ByteBuffer buffer = ByteBuffer.allocate(1000);
  channel.read(buffer, 0, buffer,
      new CompletionHandler<Integer, ByteBuffer>() {
        public void completed(Integer result, ByteBuffer attachment) {
          System.out.println(new String(buffer.array()));
        }
  }); 

通过这种方式,我可以从large.log读取前1000个字节。如果我不想分配更大的字节数组(如ByteBuffer.allocate(1000*1000) ),如何读取以下日志。因为我认为这会导致OutOfMemory。

能给我这个示例代码吗?谢谢。

ps:我可以用JIO循环读取大文件,因为我可以检查java.io.BufferedReader.read()的返回值。但我不知道如何处理NIO2。

EN

回答 4

Stack Overflow用户

发布于 2013-10-23 05:15:44

这是一个有效的黑客。

有几件事你需要注意:

  1. 我刚刚使用了您的buffer.array()作为输出。我不得不使用buffer.clear()来重置位置,以便异步读取将看到有1000个备用字节,但这并不能清除数组中的现有数据。因此,当您在文件末尾时,如果读取的字节少于1000个字节,它就会打印整个缓冲区:不管您刚刚读取了多少,再加上缓冲区末尾最后一个的其余1000个字节。在现实生活中,您可能会想做一些事情(可能是结果,也可能是缓冲区的位置)。
  2. 由于原因,我无法确定buffercompleted方法中的类变量,但是也是类变量的channel为null。我还没弄明白为什么会这样。因此,我更改了它,以便它将channel作为附件而不是缓冲区传递。对我来说还是没有意义。
  3. 异步读取线程不足以保持jvm运行。因此,我简单地将一个read放在主方法的末尾。按Enter退出。
  4. 类变量pos维护正在读取的文件中的位置。
  5. 当您在complete方法期间启动另一个异步读取时,就会发生这种神奇的情况。这就是为什么我放弃了匿名类并实现了接口本身。
  6. 你会想换回你的路。

玩得开心。

代码语言:javascript
复制
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.io.IOException;

public class TryNio implements CompletionHandler<Integer, AsynchronousFileChannel> {

       // need to keep track of the next position.
        int pos = 0;
        AsynchronousFileChannel channel =  null;
        ByteBuffer buffer = null;

        public void completed(Integer result, AsynchronousFileChannel attachment) {
                 // if result is -1 means nothing was read.
                if (result != -1) {
                        pos += result;  // don't read the same text again.
                                        // your output command.
                        System.out.println(new String(buffer.array()));

                        buffer.clear();  // reset the buffer so you can read more.
                }
                        // initiate another asynchronous read, with this.
                attachment.read(buffer, pos , attachment, this );


        }
        public void failed(Throwable exc,
                        AsynchronousFileChannel attachment) {
                System.err.println ("Error!");
                exc.printStackTrace();
        }

        public void doit() {
                Path file = Paths.get("/var/log/syslog");
                AsynchronousFileChannel channel =  null;
                try {
                        channel = AsynchronousFileChannel.open(file);
                } catch (IOException e) {
                        System.err.println ("Could not open file: " + file.toString());
                        System.exit(1); // yeah.  heh.
                }
                buffer = ByteBuffer.allocate(1000);

                 // start off the asynch read. 
                channel.read(buffer, pos , channel, this );
                // this method now exits, thread returns to main and waits for user input.
        }

        public static void main (String [] args) {
                TryNio tn = new TryNio();
                tn.doit();
             // wait fur user to press a key otherwise java exits because the 
             // asynch thread isn't important enough to keep it running.
                try { System.in.read(); } catch (IOException e) { }
        }
}
票数 6
EN

Stack Overflow用户

发布于 2018-06-14 10:10:56

GregHNZ解决方案很棒,而且由于我必须在不同的项目中多次使用这种代码,所以我最终将它放在了一个辅助库RxIo中,这是我在Maven Central repository中发布的,并且也可以在RxIo github存储库中获得。使用RxIo,可以使用RxIo实用工具类读取文件的所有字节,如下所示:

代码语言:javascript
复制
AsyncFiles
    .readAllBytes(Paths.get("input.txt"))
    .thenApply(bytes -> { /*... use bytes... */});

readAllBytes(Path file)分配默认大小为262144的ByteBuffer,但可以使用readAllBytes(Path file, int bufferSize)指定不同的值。

您可以在单元测试文件夹中看到其他用例。

票数 1
EN

Stack Overflow用户

发布于 2013-10-23 03:03:25

如果文件中还有其他内容,请在completionHandler中启动另一个读取。但我会使用比1000大得多的缓冲区,至少8192。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19532020

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档