Tob*_*obi 5 java java-8 java-stream
我使用Java 8流来处理文件,但到目前为止总是逐行处理.
我想要的是一个函数,它获取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();
Run Code Online (Sandbox Code Playgroud)
这显然使Inputstream处于第20个单词的下一行的位置.
有没有办法从输入流中获取少于一行的流?
编辑
我正在解析一个文件,其中第一个单词包含下列单词的数量.我读了这个词然后读了具体的单词数.该文件包含多个这样的部分,其中每个部分在所描述的函数中被解析.
阅读完所有有用的注释后,我很清楚,使用a Scanner
是这个问题的正确选择,Java 9将有一个Scanner
提供流功能的类(Scanner.tokens()
和Scanner.findAll()
).
以我描述的方式使用Streams将无法保证读者将在流的终端操作(API文档)之后处于特定位置,因此使得流成为解析结构的错误选择,其中您只解析一个结构部分并且必须跟踪位置.
关于你原来的问题:我假设你的文件是这样的:
5 a section of five words 3 three words
section 2 short section 7 this section contains a lot
of words
Run Code Online (Sandbox Code Playgroud)
你想得到这样的输出:
[a, section, of, five, words]
[three, words, section]
[short, section]
[this, section, contains, a, lot, of, words]
Run Code Online (Sandbox Code Playgroud)
通常,Stream API非常适合此类问题.编写普通的旧循环在这里看起来更好.如果您仍想查看基于Stream API的解决方案,我可以建议使用我的StreamEx库,其中包含headTail()
允许您轻松编写自定义流转换逻辑的方法.以下是使用以下方法解决问题的方法headTail
:
/* 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);
});
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
String s = "5 a section of five words 3 three words\n"
+ "section 2 short section 7 this section contains a lot\n"
+ "of words";
Reader reader = new StringReader(s);
Stream<List<String>> stream = records(StreamEx.ofLines(reader)
.flatMap(Pattern.compile("\\s+")::splitAsStream));
stream.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
结果与上面所需的输出完全一致.替换reader
为您BufferedReader
或FileReader
从输入文件中读取.记录流是懒惰的:一次最多只有一条记录由流保存,如果你短路,其余的输入将不会被读取(当然,当前文件行将被读取到最后).该解决方案虽然看起来递归,但不会占用堆栈或堆,因此它也适用于大型文件.
说明:
该headTail()
方法采用两参数lambda,当请求流元素时,该参数在外部流终端操作执行期间最多执行一次.lambda接收第一个流元素(head)和包含所有其他原始元素(tail)的流.lambda应返回一个新流,而不是原始流.在records
我们有:
return input.headTail((count, tail) ->
makeRecord(tail, Integer.parseInt(count), new ArrayList<>()));
Run Code Online (Sandbox Code Playgroud)
输入的第一个元素是count
:将其转换为数字,创建为空ArrayList
并调用makeRecord
尾部.这是makeRecord
辅助方法实现:
return input.headTail((head, tail) -> {
Run Code Online (Sandbox Code Playgroud)
第一个流元素是head
,将它添加到当前缓冲区:
buf.add(head);
Run Code Online (Sandbox Code Playgroud)
达到目标缓冲区大小?
return buf.size() == count
Run Code Online (Sandbox Code Playgroud)
如果是,records
则tail
再次调用for (处理下一条记录,如果有的话),并在结果流中添加单个元素:当前缓冲区.
? records(tail).prepend(buf)
Run Code Online (Sandbox Code Playgroud)
否则,调用自己的尾部(向缓冲区添加更多元素).
: makeRecord(tail, count, buf);
});
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3057 次 |
最近记录: |