非阻塞(NIO)读取行

14

我需要使用 java.nio 逐行读取文件,但 nio 没有像 readline() 这样的方法可以一次性读取一整行。有哪些解决方案?


4
为什么你需要使用Java NIO来做这件事,你认为这样做会比使用“BufferedReader”获得更多好处吗? - Jesper
@Jesper 是的..你能告诉我如何读取像.readline()这样的一行吗? - Aashwin Jain
一个示例代码将非常有帮助。 - Aashwin Jain
让我换个说法:你认为使用NIO而不是BufferedReader会带来什么好处? - Jesper
使用Socket上的readLine http://docs.oracle.com/javase/tutorial/networking/sockets/readingWriting.html https://dev59.com/-0nSa4cB1Zd3GeqPRdSa https://dev59.com/TnE85IYBdhLWcg3waCxT - Peter Lawrey
@Jesper 我想进行比较..随意的研究和开发..你能给我一个逐行读取的代码片段吗? - Aashwin Jain
9个回答

17

我知道你们不喜欢限制,但是如果提问者没有访问IO包的权限,或者由于某些原因不能导入该包,则排名前几位的答案并不实用...

有两种完全不需要IO的方法:

  1. java.nio.file.Files.lines , 返回一系列行,这是.util包中的一部分,而不是像bufferedReader那样在.io包中。

  2. java.nio.file.Files.readAllLines , 返回可迭代的一系列行。使用iteratorfor each来提取单行内容。

干杯


3
“如果询问者没有访问IO包的权限”是完全虚构的限制。 - user207421
3
大学的练习有时候限制很奇怪。 - Daniel Braun
@user207421nope。今天我发现自己处于同样的情况,就像Daniel Braun所说的那样。 - user6214276
你提到的这两种方法(Files.linesFiles.readAllLines)实际上都是使用java.io.BufferedReader实现的。因此,我不确定这对于想要使用非阻塞IO的OP有什么用处。 - typeracer

5

为什么?NIO 不支持读取行。你可以使用 BufferedReader.readLine() 每秒读取数百万行。我建议这已经足够了。


3
Oracle在教程中介绍了一个示例。 https://docs.oracle.com/javase/tutorial/essential/io/file.html#streams
Path file = ...;
try (InputStream in = Files.newInputStream(file);
    BufferedReader reader =
      new BufferedReader(new InputStreamReader(in))) {
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException x) {
    System.err.println(x);
}

3
使用 java.nio.file.Files,您可以进行以下操作:
Path path = FileSystems.getDefault().getPath("/path/to", "file.txt");
Files.lines(path).forEach(line ->
    // consume line
);

由于lines(path)方法返回一个Stream,因此您可以利用Stream API的任何其他方法,例如只读取第一行(如果存在):

Optional<String> firstLine = Files.lines(path).findFirst();

如果您查看源代码,Files.lines() 是使用普通的 BufferedReader 实现的,因此仍然具有老式的阻塞 IO。顺便说一下,BufferedReader 还有一个 lines() 方法,允许将其与 Stream API 一起使用。 - typeracer

2

对我来说它有效....


 public static void main(String[] args) throws IOException 
    {
        RandomAccessFile aFile = new RandomAccessFile
                ("F:\\DetailsMy1.txt", "r");
        FileChannel inChannel = aFile.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1);
        StringBuffer line = new StringBuffer();
        while(inChannel.read(buffer) > 0)
        {
            buffer.flip();
            for (int i = 0; i < buffer.limit(); i++)
            {
                char ch = ((char) buffer.get());
                if(ch=='\r'){
                    System.out.print(line+"[EOL]");
                    line=new StringBuffer();
                }else{
                    line.append(ch);
                }
            }
            buffer.clear(); // do something with the data and clear/compact it.
        }
        inChannel.close();
        aFile.close();
    }

4
这段代码读取了一个字节一次,这不是使用java.nio的初衷吗? - Berlin Brown
而且你完全避免了字符集的问题。 - user207421
这是一个完美的例子,说明为什么这样的代码是个坏主意。你有一个错误。最后一行停留在StringBuffer中而没有被打印或者其他处理。 - Przemek

1

支持EJP和其他人的观点,你可以参考这篇文章:http://www.javaworld.com/article/2078654/java-se/java-se-five-ways-to-maximize-java-nio-and-nio-2.html

特别是:

“虽然NIO通常因其性能优势而受到推广,但更准确地说,它具有高度响应能力。在某些情况下,NIO实际上比基本的Java I/O表现更差。例如,对于小文件的简单顺序读写,直接使用流实现可能比相应的面向事件的基于通道的编码快两到三倍。此外,非多路复用通道--也就是在单独的线程中使用的通道--可能比在单个线程中注册其选择器的通道慢得多。”

我想强调的是NIO“在某些情况下[...]表现比基本的Java I/O更差”的说法。作者随后列出了一系列问题,以进行快速分析,以确定是否选择NIO。如果您(或下一个人)仍然决定追求NIO,则有一个很好的解释及其使用代码示例。


1

1)您可以使用lines方法将BufferedReader转换为Stream

List<String> list = new ArrayList<>();

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    list = br.lines().collect(Collectors.toList());    
}

2)使用Files.lines方法直接获取流:

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
    stream
        .filter(s -> s.endswith("/"))
        .map(String::toUpperCase)
        .forEach(System.out::println);
}

正如DanielBraun在他的回答中指出的那样,您也可以使用readAllLines方法而不是lines方法,两者的区别在于,直接从文档中得知:

与readAllLines不同,此方法不会将所有行读入List中, 而是在流被消耗时进行懒惰填充。

Java文档中的package summary页面提供了有关Streams的简洁概述,而这篇文章还列出了一些其他在Java中按行读取文件的方法。


0

NIO通常用于直接内存访问或块介质批量数据传输。它确实还有其他功能,但其他功能更多涉及阻塞和非阻塞数据访问。

因此,您可能希望使用NIO快速抓取数据(或以非阻塞方式),但是,如果您想"逐行读取",最好在NIO读取可用数据后执行行检测。这可以通过在NIO刚刚读取的缓冲区上放置一个"行读取"外观来轻松实现。


在NIO刚刚读取的缓冲区上放置一个“行读取”外观。 - Aashwin Jain
没有人会给你答案,因为当你深入了解如何做到这一点时,它就开始更加依赖很多小细节(例如如何处理NIO缓冲区)。当然,你可以始终使用NIO缓冲区支持基于流的读取器;但这就引出了一个问题,你为什么要使用NIO呢? - Edwin Buck

-1

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