Java8流:逐字读取文件

Java8流:逐字读取文件,java,java-8,java-stream,Java,Java 8,Java Stream,我经常使用Java8流来处理文件,但到目前为止总是逐行处理 我想要的是一个函数,它获取一个BufferedReader br,应该读取特定数量的单词(以“\\s+”分隔),并且应该将BufferedReader保留在达到单词数量的确切位置 现在我有了一个版本,它逐行读取文件: final int[] wordCount = {20}; br .lines() .map(l -> l.split("\\s+")) .

我经常使用Java8流来处理文件,但到目前为止总是逐行处理

我想要的是一个函数,它获取一个
BufferedReader br
,应该读取特定数量的单词(以
“\\s+”
分隔),并且应该将BufferedReader保留在达到单词数量的确切位置

现在我有了一个版本,它逐行读取文件:

    final int[] wordCount = {20};
    br
          .lines()
          .map(l -> l.split("\\s+"))
          .flatMap(Arrays::stream)
          .filter(s -> {
              //Process s
              if(--wordCount[0] == 0) return true;
              return false;
          }).findFirst();

这显然会将Inputstream保留在 第20个单词。
有没有一种方法可以从inputstream获取读取量小于一行的流

编辑
我正在解析一个文件,其中第一个单词包含以下单词的数量。我读了这个单词,然后相应地读了具体的单词数。该文件包含多个这样的部分,其中每个部分都在所描述的函数中进行解析

在阅读了所有有用的评论之后,我清楚地认识到,使用
扫描仪
是解决这个问题的正确选择,Java 9将有一个
扫描仪
类,它提供流特性(
Scanner.tokens()
Scanner.findAll()
)。

按照我描述的方式使用Streams,我无法保证读卡器在stream()的终端操作后会处于特定位置,因此使Streams成为解析结构的错误选择,您只解析一个部分,并且必须跟踪位置。

关于原始问题:我假设您的文件如下所示:

5 a section of five words 3 three words
section 2 short section 7 this section contains a lot 
of words
[a, section, of, five, words]
[three, words, section]
[short, section]
[this, section, contains, a, lot, of, words]
您希望得到如下输出:

5 a section of five words 3 three words
section 2 short section 7 this section contains a lot 
of words
[a, section, of, five, words]
[three, words, section]
[short, section]
[this, section, contains, a, lot, of, words]
一般来说,流API非常适合此类问题。在这里编写简单的旧循环看起来是一个更好的解决方案。如果您仍然希望看到基于流API的解决方案,我可以建议使用我的库,它包含允许您轻松编写自定义流转换逻辑的方法。下面是如何使用
头尾
解决您的问题:

/* Transform Stream of words like 2, a, b, 3, c, d, e to
   Stream of lists like [a, b], [c, d, e] */
public static StreamEx<List<String>> records(StreamEx<String> input) {
    return input.headTail((count, tail) -> 
        makeRecord(tail, Integer.parseInt(count), new ArrayList<>()));
}

private static StreamEx<List<String>> makeRecord(StreamEx<String> input, int count, 
                                                 List<String> buf) {
    return input.headTail((head, tail) -> {
        buf.add(head);
        return buf.size() == count 
                ? records(tail).prepend(buf)
                : makeRecord(tail, count, buf);
    });
}
输入的第一个元素是
count
:将其转换为数字,创建空的
ArrayList
,并调用
makeRecord
作为尾部。下面是
makeRecord
helper方法实现:

return input.headTail((head, tail) -> {
第一个流元素是
,将其添加到当前缓冲区:

    buf.add(head);
是否达到目标缓冲区大小

    return buf.size() == count 
如果是,请再次调用
尾部的
记录
(处理下一条记录,如果有),并使用单个元素:current buffer预先结束结果流

            ? records(tail).prepend(buf)
否则,调用我自己的尾部(向缓冲区添加更多元素)


“这显然将Inputstream保留在第20个字的下一行的位置。”执行此管道后,输入流处于什么状态还很不明显。我假设这条流是EOF,你确定吗?在执行终端流操作后,无法保证读取器将位于读取下一个字符或行的特定位置。为什么不使用
扫描仪
并使用
扫描仪读取每个单词。next()
?@Tunaki加上一个用于
扫描仪
。但是请注意,如果使用
扫描仪
处理或跳过前N个单词,则必须继续使用
扫描仪
处理以下内容。原因是
Scanner
缓冲来自其源的数据,即使该源已经有了自己的缓冲区。如果能看到您原来的问题,那就太好了。为什么需要在word#20之后停止
BufferedReader
?那之后你想用它做什么?也许可以用其他方法解决。那真是一个很棒的图书馆!!
            : makeRecord(tail, count, buf);
});