2017-09-10 3 views
2

以下のように複数の正規表現を使用したいのですが、どのようにフラット・マップ・イテレータに追加して、Java 8ストリームで複数のRegexを使って行からテキストを読み取る

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); 
} 

が、これが意味するものではありかもしれないだけでその、各ラインを通じて3件の個別の検索を実行することに注意してください:

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

から拡張したものです1行内の一致の順序が実際の発生を反映しないことを意味します。あなたのパターンには問題はないようですが、個々の検索では重複している可能性があることを暗示しています。

また、このリンク解答のPatternStreamerは、ストリームを作成する前に、1つの文字列の一致を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; 
    } 
} 

この施設は、A内の複数のパターンと一致することを可能にします(pattern1|pattern2|pattern3)ファッションの各パターンの元のグループを持っています。したがって、hellと​​をhelloに検索すると、hellで、​​ではなくなります。異なる点は、複数のパターンが同じ位置で一致する場合、保証された順序がないことです。

これは、オーバーロードされたメソッドは、ストリームを作成するために、パターン文字列を使用してMultiPatternSpliterator.matches(s, reTimeStamp, reHostName, reServiceTime)を使用することができるようになるが、これはすべての入力のためのすべての正規表現を再コンパイルしますflatMap運転中に避けるべきである

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); 
} 

ように使用することができますライン。そのため、上記のコードはすべてのパターンを最初に配列にコンパイルするのです。これは、ストリーム操作の外側でPatternStreamerをインスタンス化することによって元のコードも同様に動作します。

+0

これは精巧です... – Eugene

+0

素敵な説明です。 –

+0

大きなファイル(5GB)でのみ読み込み中に奇妙な動作に気づいた場合、ストリームが2つのパターン(例えばStream.of(reTimeStamp、reHostName))と一致する場合、ストリームは10分以内にファイル全体を完璧に読み取ることができました出力を印刷します。 Stream.of(reTimeStamp、reHostName、reServiceTime)のような3番目のパターンを追加して、同じファイルに再度実行すると、ファイルがメモリに永久に保存され(VisualVMで監視され)、エラーでクラッシュしなくなります。これはStream.concat(regex1、regex2)の場合と同じように動作します。 Stream.concat(regex1、regex2、regex3) - Javaプロセスがハングします。 – Shan

関連する問題