2017-09-10 27 views
2

我想要如下所示的多个正则表达式,我如何添加到flatmap迭代器,以便在单个流读取期间将行的所有匹配值放入List?在Java 8中有多个正则表达式来读取文本行

static String reTimeStamp="((?:2|1)\\d{3}(?:-|\\/)(?:(?:0[1-9])|(?:1[0-2]))(?:-|\\/)(?:(?:0[1-9])|(?:[1-2][0-9])|(?:3[0-1]))(?:T|\\s)(?:(?:[0-1][0-9])|(?:2[0-3])):(?:[0-5][0-9]):(?:[0-5][0-9]))"; 
static String reHostName="host=(\\\")((?:[a-z][a-z\\.\\d\\-]+)\\.(?:[a-z][a-z\\-]+))(?![\\w\\.])(\\\")"; 
static String reServiceTime="service=(\\d+)ms"; 

private static final PatternStreamer quoteRegex1 = new PatternStreamer(reTimeStamp); 
private static final PatternStreamer quoteRegex2 = new PatternStreamer(reHostName); 
private static final PatternStreamer quoteRegex3 = new PatternStreamer(reServiceTime); 


public static void main(String[] args) throws Exception { 
    String inFileName = "Sample.log"; 
    String outFileName = "Sample_output.log"; 
    try (Stream<String> stream = Files.lines(Paths.get(inFileName))) { 
     //stream.forEach(System.out::println); 
     List<String> timeStamp = stream.flatMap(quoteRegex1::results) 
            .map(r -> r.group(1)) 
            .collect(Collectors.toList()); 

     timeStamp.forEach(System.out::println); 
     //Files.write(Paths.get(outFileName), dataSet); 
    } 
} 

来自这个问题Match a pattern and write the stream to a file using Java 8 Stream

回答

3

一个扩展,您可以简单地串联流:

String inFileName = "Sample.log"; 
String outFileName = "Sample_output.log"; 
try (Stream<String> stream = Files.lines(Paths.get(inFileName))) { 
    List<String> timeStamp = stream 
     .flatMap(s -> Stream.concat(quoteRegex1.results(s), 
         Stream.concat(quoteRegex2.results(s), quoteRegex3.results(s)))) 
     .map(r -> r.group(1)) 
     .collect(Collectors.toList()); 

    timeStamp.forEach(System.out::println); 
    //Files.write(Paths.get(outFileName), dataSet); 
} 

但请注意,这将通过各行执行三个独立的搜索,这可能不仅意味着性能较低,而且一行内的匹配顺序不会反映其实际发生的情况。它似乎不是你的模式的问题,但个人搜索甚至意味着可能的重叠匹配。

该链接答案的PatternStreamer也会在创建流之前贪婪地将一个字符串的匹配项收集到ArrayList中。像this answer这样基于Spliterator的解决方案是优选的。

由于数值组的引用排除只是在一个(pattern1|pattern2|pattern3)方式组合的图案,在多个不同的图案匹配一个真正的流将是更进一步的描述:

public final class MultiPatternSpliterator 
extends Spliterators.AbstractSpliterator<MatchResult> { 
    public static Stream<MatchResult> matches(String input, String... patterns) { 
     return matches(input, Arrays.stream(patterns) 
       .map(Pattern::compile).toArray(Pattern[]::new)); 
    } 
    public static Stream<MatchResult> matches(String input, Pattern... patterns) { 
     return StreamSupport.stream(new MultiPatternSpliterator(patterns,input), false); 
    } 
    private Pattern[] pattern; 
    private String input; 
    private int pos; 
    private PriorityQueue<Matcher> pendingMatches; 

    MultiPatternSpliterator(Pattern[] p, String inputString) { 
     super(inputString.length(), ORDERED|NONNULL); 
     pattern = p; 
     input = inputString; 
    } 

    @Override 
    public boolean tryAdvance(Consumer<? super MatchResult> action) { 
     if(pendingMatches == null) { 
      pendingMatches = new PriorityQueue<>(
       pattern.length, Comparator.comparingInt(MatchResult::start)); 
      for(Pattern p: pattern) { 
       Matcher m = p.matcher(input); 
       if(m.find()) pendingMatches.add(m); 
      } 
     } 
     MatchResult mr = null; 
     do { 
      Matcher m = pendingMatches.poll(); 
      if(m == null) return false; 
      if(m.start() >= pos) { 
       mr = m.toMatchResult(); 
       pos = mr.end(); 
      } 
      if(m.region(pos, m.regionEnd()).find()) pendingMatches.add(m); 
     } while(mr == null); 
     action.accept(mr); 
     return true; 
    } 
} 

该工具允许以匹配在多模式(pattern1|pattern2|pattern3)时尚,同时仍然有每个模式的原始组。因此,当在hello中搜索hell和​​时,它会找到hell而不是​​。不同之处在于,如果多个模式在相同位置匹配,则没有保证顺序。

这可以用来像

Pattern[] p = Stream.of(reTimeStamp, reHostName, reServiceTime) 
     .map(Pattern::compile) 
     .toArray(Pattern[]::new); 
try (Stream<String> stream = Files.lines(Paths.get(inFileName))) { 
    List<String> timeStamp = stream 
     .flatMap(s -> MultiPatternSpliterator.matches(s, p)) 
     .map(r -> r.group(1)) 
     .collect(Collectors.toList()); 

    timeStamp.forEach(System.out::println); 
    //Files.write(Paths.get(outFileName), dataSet); 
} 

虽然重载方法将允许使用MultiPatternSpliterator.matches(s, reTimeStamp, reHostName, reServiceTime)使用图案字符串来创建流,这应该一个flatMap操作中被避免,将重新编译每正则表达式对每个输入线。这就是为什么上面的代码首先将所有模式编译成数组的原因。这是您的原始代码通过在流操作之外实例化PatternStreamer所做的工作。

+0

哦,这是精心制作的... – Eugene

+0

很好的解释。 –

+0

如果我仅匹配2个模式(例如,Stream.of(reTimeStamp,reHostName)),那么在仅读取大文件(5GB)时也会注意到一个奇怪的行为,该流能够在10分钟内完美地读取整个文件并打印输出。当我添加像Stream.of(reTimeStamp,reHostName,reServiceTime)这样的第三个模式并且在同一个文件java进程上再次运行的那一刻,通过将文件永久保存在内存中(通过VisualVM监视)并且不会因任何错误而崩溃。这与Stream.concat(regex1,regex2)的工作方式相同。 Stream.concat(regex1,regex2,regex3) - Java进程挂起。 – Shan