我遇到了流的dropWhile
或takeWhile
方法的问题,这是因为分隔符以特定的模式跳过奇数或偶数的文本部分。如何处理文本的所有部分?
我的方法在这里:
void read(Path filePath) {
try {
Stream<String> lines = Files.lines(filePath);
while (true) {
Spliterator<String> spliterator = lines.dropWhile(line -> !line.startsWith("FAYSAL:")).spliterator();
Stream<String> portion = fetchNextPortion(spliterator);
if(spliterator.estimateSize() == 0)
break;
portion .forEach(System.out::println);
lines = StreamSupport.stream(spliterator, false);
}
lines.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
private Stream<String> fetchNextPortion(Spliterator<String> spliterator) {
return StreamSupport.stream(spliterator, false)
.filter(this::isValidReportName)
.peek(System.out::println)
.findFirst()
.map( first -> Stream.concat(Stream.of(first),
StreamSupport.stream(spliterator, false).takeWhile(line -> !line.startsWith("FAYSAL:")))).orElse(Stream.empty());
}
示例输入为:
FAYSAL: 1
Some text here
Some text here
FAYSAL: 2
Some text here
Some text here
FAYSAL: 3
Some text here
Some text here
FAYSAL: 4
Some text here
Some text here
它将跳过FAYSAL:2和FAYSAL:4
答案 0 :(得分:2)
如何处理文本的所有部分?
您可以选择其他方法。
您的代码在显示您的问题后在我的机器上产生了StackOverflowError(也有对fetchNextChunk
的调用,但有一个fetchNextPartition
的方法,因此我也不确定),因此,尝试调试它时,我想出了另一种分割输入的方法。鉴于我的方法将整个String包含在内存中,因此它可能不适用于较大的文件。以后我可能会用Streams制定一个版本。
基本假设:您想将输入文本分成多个部分,每个部分都以一个以“ FAYSAL:”开头的字符串开头。
这个想法与您的方法类似,但是不基于Spliterators,也没有使用dropWhile。相反,它找到以“ FAYSAL:”开头的第一个字符串(我以为isValidReportName
是这样做的;该方法的代码不在问题之列),然后将所有内容直接放入下一部分。将找到的第一个元素添加为列表的第一个元素,然后将该集合添加到一个列表中,以便以后使用。然后从原始列表中删除收集的行数。
完整代码:
import java.util.*;
import java.util.stream.Collectors;
class Main {
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.partitionTextByStringStart(m.getString()));
}
private List<List<String>> partitionTextByStringStart(String text) {
List<List<String>> partitions = new ArrayList<>();
List<String> lines = Arrays.asList(text.split("\n"));
while (!lines.isEmpty()) {
String first = lines.stream().filter(this::isValidReportName).findFirst().orElse("This is prolly bad");
List<String> part = lines.stream().skip(1).takeWhile(l -> !l.startsWith("FAYSAL:")).collect(Collectors.toList());
part.add(0, first);
partitions.add(part);
lines = lines.subList(part.size(), lines.size());
}
return partitions;
}
private boolean isValidReportName(String x) {
return x.startsWith("FAYSAL:");
}
private String getString() {
return "FAYSAL: 1\n" +
"Some text here1\n" +
"Some text here1\n" +
"FAYSAL: 2\n" +
"Some text here2\n" +
"Some text here2\n" +
"FAYSAL: 3\n" +
"Some text here3\n" +
"Some text here3\n" +
"FAYSAL: 4\n" +
"Some text here4\n" +
"Some text here4";
}
}
(注意:我在这里使用静态字符串而不是文件读取来制作完整的代码示例;您可以相应地修改代码)
编辑:经过一些研究,我发现使用名为StreamEx(Github)(Maven)的库将流中的内容分组非常容易。在this答案中,我找到了有关StreamEx#groupRuns
函数的注释,该注释正是这样做的:
private Stream<Stream<String>> partitionStreamByStringStart(Stream<String> lineStream) {
return StreamEx.of(lineStream).groupRuns((l1, l2) -> !l2.startsWith("FAYSAL:")).map(Collection::stream);
}
要使其正常运行,您可以添加
System.out.println(m.partitionStreamByStringStart(m.getStream()).map(
s -> s.collect(Collectors.toList())
).collect(Collectors.toList()));
主要功能和
private Stream<String> getStream() {
return Stream.of(getString().split("\n"));
}
在上面完整代码示例的Main类中的某个位置。