Java 8流式处理中如何发出一个流?

5

我有以下文件格式:

Text1
+ continuation of Text1
+ more continuation of Text1 
Text2
+ continuation of Text2
+ more continuation of Text2
+ even more continuation of Text2

续行由\n+标记。(换行符、加号、空格组成的三个字符字符串)。续行可以是任意数量的行,包括0行。

我想要以下输出结果(每行都用.forEach打印出来):

Text1 continuation of Text1 more continuation of Text1 
Text2 continuation of Text2 more continuation of Text2 even more continuation of Text2

我希望只使用Java流来进行转换,最好使用Collect。有没有一种优雅的方法来实现这个?
编辑:
另一个更现实的例子:
Lorem ipsum dolor sit amet, consectetur 
+ adipiscing elit, sed do eiusmod tempor incididunt 
+ ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex 
+ ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit 
+ esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 
+ occaecat cupidatat non proident, sunt in culpa qui officia 
+ deserunt mollit anim id est laborum.

预期结果应该是两行:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

1
假设第一行总是第一个分隔符,下一行不包含此分隔符,这种做法正确吗?我的意思是说,“Text1”的后续部分是否真的包含“Text1”,还是只是为了让示例更清晰? - Eugene
不,那只是为了澄清。 - TFuto
1
  1. 那么,第一行总是分隔符吗?是/否
  2. 分隔符不包含在下一行中吗?是/否 :)
- Eugene
  1. 不,第一行不是分隔符。它是任意文本。
  2. 连接只显示为“\n+”,所以换行符、加号和空格。我添加了另一个示例。
- TFuto
1
可能是可行的,但不适合于流,因为它们无法引用先前的元素,并且最好具有无状态映射。在这里,老派的循环是更好的选择。 - Bohemian
2个回答

10
在Java 9中,你可以使用
static final Pattern LINE_WITH_CONTINUATION = Pattern.compile("(\\V|\\R\\+)+");

try(Scanner s = new Scanner(file)) {
    s.findAll(LINE_WITH_CONTINUATION)
        .map(m -> m.group().replaceAll("\\R\\+", ""))
        .forEach(System.out::println);
}


由于Java 8缺少Scanner.findAll(Pattern)方法,您可以添加一个自定义实现的操作作为解决方法。

public static Stream<MatchResult> findAll(Scanner s, Pattern pattern) {
    return StreamSupport.stream(new Spliterators.AbstractSpliterator<MatchResult>(
            1000, Spliterator.ORDERED|Spliterator.NONNULL) {
        public boolean tryAdvance(Consumer<? super MatchResult> action) {
            if(s.findWithinHorizon(pattern, 0)!=null) {
                action.accept(s.match());
                return true;
            }
            else return false;
        }
    }, false);
}

可以像这样使用
try(Scanner s = new Scanner(file)) {
    findAll(s, LINE_WITH_CONTINUATION)
        .map(m -> m.group().replaceAll("\\R\\+", ""))
        .forEach(System.out::println);
}

这将使未来的迁移变得容易。


2
假设您只按顺序运行此代码,并且真的想使用流:
 List<String> result = Files.lines(Paths.get("YourPath"))
            .collect(() -> new ArrayList<>(), (list, line) -> {
                int listSize = list.size();
                if (line.startsWith("+ ")) {
                    list.set(listSize - 1, list.get(listSize - 1) + line.substring(2));
                } else {
                    list.add(line);
                }
            }, (left, right) -> {
                throw new RuntimeException("Not for parallel processing");
            });

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