我想读取一个由“块”组成的ASCII文件,这些块由开始和结束标记分隔。
我从未使用过Java 8流,我想在这个文件阅读器上测试它们,但我真的不知道怎么做。
为简单起见,我们考虑以下文件格式(实际文件格式can be found here):
$Node
6
1 1.0 0.0 0.0
2 -1.0 0.0 0.0
3 0.0 1.0 0.0
4 0.0 -1.0 0.0
5 0.0 0.0 1.0
6 0.0 0.0 -1.0
$EndNode
$Elements
3
1 10 1 2 3
2 10 4 5 6
3 10 1 5 3
$EndElements
每个块的第一行是块中元素的数量。然后每个块都是以空格分隔的值列表。值和类型的数量取决于块。
在现实生活中,文件可能会变得非常大(几百Mb,可能高达几Gb),因此性能至关重要。
使用Java NIO 2(没有Java 8流),我会做这样的事情:
BufferedReader reader = Files.newBufferedReader(filePath, Charset.defaultCharset());
String line = null;
Parser currentParser = defaultParser;
while ((line = reader.readLine()) != null) {
if (line.startsWith("$")) {
currentParser = getParser(line);
continue;
}
currentParser.parseLine(line);
}
使用一个足够聪明的线解析器来处理块的第一行与其余部分不同(不必为每一行测试一个isFirstLineOfBlock布尔值)......不知道如何做到这一点顺便说一句。
无论如何,我很感激为这个文件阅读器使用Java 8流提供了一些帮助。
最后一个问题,在这样的应用程序中使用Java流有什么好处:它只是一个可读性问题,还是我期望改进性能?
答案 0 :(得分:1)
有几种方法可以使用Java 8流。例如,
try (BufferedReader br = Files.newBufferedReader(Paths.get(filePath, Charset.defaultCharset())) {
br.lines()
.filter(line -> !line.startsWith("$"))
.forEachOrdered(currentParser::parseLine);
} catch (IOException ex) {
throw new Error(ex);
}
.lines()方法说明包含
The Stream is lazily populated, i.e.,
read only occurs during the terminal stream operation.
在此示例中,终端操作为forEachOrdered
try (Stream<String> stream = Files.lines(Paths.get(filePath, Charset.defaultCharset())) {
stream
.filter(line -> !line.startsWith("$"))
.forEachOrdered(currentParser::parseLine);
} catch (IOException ex) {
throw new Error(ex);
}
答案 1 :(得分:1)
可以在我的免费StreamEx库的帮助下解析此类构造,该库增强了标准Stream API:
StreamEx.ofLines(filePath, Charset.defaultCharset())
.groupRuns((a, b) -> !b.startsWith("$"))
.forEachOrdered(list ->
list.subList(1, list.size()).forEach(getParser(list.get(0))::parseLine));
这里我们使用groupRuns
方法将单个文件条目组合到列表中。传递给groupRuns
的参数是BiPredicate
,它适用于相邻的输入元素对,如果必须对元素进行分组,则返回true。在这里,我们对元素进行分组,除非下一个元素以"$"
开头。之后我们已经懒惰填充Stream<List<String>>
并解析每个组使用第一行创建解析器并为所有后续行调用parseLine
。