【问题标题】:Split file in chunk when fine head record (java 8)细头记录时将文件拆分为块(java 8)
【发布时间】:2018-03-23 08:47:05
【问题描述】:

我有一段代码在找到开始记录时将文件“拆分”成一些块。

List<StringBuilder> list = new ArrayList<>();
StringBuilder jc = null;
try (BufferedReader br = Files.newBufferedReader(Paths.get("")) {
    for (String line = br.readLine(); line != null; line = br.readLine()) {
        if (line.startsWith("REQ00")) {
            jc = new StringBuilder();
            list.add(jc);
        }
        jc.append(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

有没有办法将这段代码“转换”成 Java 8 Stream 方式?

【问题讨论】:

  • 首先,这段代码有很多错误,而且代码的行为似乎不像你解释的那样。你介意修理那些吗?例如,如果第一行不以 REQ00 开头,则此代码将具有 NPE,因为 jc.append(line)
  • 试试try (Stream&lt;String&gt; stream = Files.lines(Paths.get(""))) { stream.filter(line-&gt;line.startsWith("REQ00")).collect(Collectors.toList()); }
  • @pvpkiran 你说得对,这段代码战写得这么快就解释了我想要什么。放轻松;)
  • @HadiJ 我不想只收集“REQ00”记录,但我想从 REQ00 获取所有记录到另一个
  • 第一行是否总是以 REQ00 开头?

标签: java lambda java-8


【解决方案1】:

为工作使用正确的工具。有了Scanner,就这么简单

List<String> list = new ArrayList<>();
try(Scanner s = new Scanner(Paths.get(path))) {
    s.useDelimiter(Pattern.compile("^(?=REQ00)", Pattern.MULTILINE));
    while(s.hasNext()) list.add(s.next());
} catch (IOException e) {
    e.printStackTrace();
}

现在您的代码具有创建StringBuilders 和不保留换行符的特殊要求。所以扩展版是:

List<StringBuilder> list = new ArrayList<>();
try(Scanner s = new Scanner(Paths.get(path))) {
    s.useDelimiter(Pattern.compile("^(?=REQ00)", Pattern.MULTILINE));
    while(s.hasNext()) list.add(new StringBuilder(s.next().replaceAll("\\R", "")));
} catch (IOException e) {
    e.printStackTrace();
}

一个更有效的变体是

List<StringBuilder> list = new ArrayList<>();
try(Scanner s = new Scanner(Paths.get(path))) {
    s.useDelimiter(Pattern.compile("^(?=REQ00)", Pattern.MULTILINE));
    while(s.hasNext()) list.add(toStringBuilderWithoutLinebreaks(s.next()));
} catch (IOException e) {
    e.printStackTrace();
}

…

static final Pattern LINE_BREAK = Pattern.compile("\\R");
static StringBuilder toStringBuilderWithoutLinebreaks(String s) {
    Matcher m = LINE_BREAK.matcher(s);
    if(!m.find()) return new StringBuilder(s);
    StringBuilder sb = new StringBuilder(s.length());
    int last = 0;
    do { sb.append(s, last, m.start()); last = m.end(); } while(m.find());
    return sb.append(s, last, s.length());
}

从 Java 9 开始,您还可以对其使用 Stream 操作:

List<StringBuilder> list;
try(Scanner s = new Scanner(Paths.get(path))) {
    list = s.useDelimiter(Pattern.compile("^(?=REQ00)", Pattern.MULTILINE))
            .tokens()
            .map(string -> toStringBuilderWithoutLinebreaks(string))
            .collect(Collectors.toList());
} catch (IOException e) {
    e.printStackTrace();
    list = List.of();
}

【讨论】:

  • 因为它能够跨行处理文本。在为您的任务处理行流时,您需要跨多个流元素工作。相比之下,Scanner 产生多行元素,从出现的分隔符到下一个分隔符。如果您不需要消除换行符(至少,您的原始代码消除了它们),那么扫描仪生成的字符串已经是最终结果,比将文本分成几行然后再加入它们要高效得多。
【解决方案2】:
Map<Integer, String> chunks = Files.lines(Paths.get("")).collect(
    Collectors.groupingBy(
        new Function<String, Integer>(){
            Integer lastKey = 0;
            public Integer apply(String s){
                if(s.startsWith("REQ00")){
                    lastKey = lastKey+1;
                }
                return lastKey;
            }
        }, Collectors.joining()));

我刚刚使用了连接,它创建了一个字符串而不是一个字符串生成器。它可以替换为使用字符串生成器的收集器,或者之后可以将字符串更改为字符串生成器。

【讨论】:

  • 这假设函数以正确的顺序进行评估,但不能保证......
  • 是否会订购函数的评估?如果流不是“无序的”或并发的。并且收集器没有标记为并发,似乎该函数将按顺序调用。我正在尝试从here 和您链接的后续文档中关注您的回答。但我不清楚。
  • 函数求值的顺序是处理顺序。它永远无法保证。对于有序流,遇到顺序保持不变,这意味着最终的结果会体现出来。这仅适用于函数产生正确结果的情况,而不管它们的评估顺序如何。您的代码可能会在顺序评估中产生预期的结果(尽管不能保证),但在并行评估中肯定会中断(嗯,几乎可以肯定,因为即使这样也不能保证)。
猜你喜欢
  • 1970-01-01
  • 2018-09-14
  • 1970-01-01
  • 2014-03-08
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多