2017-09-10 3 views
2

다음과 같이 정규 표현식을 하나 이상 갖고 싶습니다. 단일 스트림을 읽는 동안 행의 일치하는 모든 값을 List에 넣으려면 어떻게해야합니까?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); 
} 

그러나 이것은 의미 수뿐만 아니라, 이는 각 라인을 통해 세 가지 개별 검색을 수행합니다 참고 :

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

에서 확장 성능은 낮지 만 한 행 내에서 일치하는 순서가 실제 발생을 반영하지는 않습니다. 그것은 당신의 패턴에 문제가있는 것 같지 않지만, 개별 검색은 중복되는 가능한 일치를 암시합니다.

해당 답변의 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) 패션은 여전히 ​​각 패턴의 원래 그룹을 갖습니다. 따라서 hellllohello에서 검색 할 경우 hell이고 llo이 아니라는 것을 알 수 있습니다. 차이점은 둘 이상의 패턴이 동일한 위치에서 일치하는 경우 보장 된 순서가 없음을 의미합니다. 오버로드 된 메소드가 모든 입력마다 정규식 재 컴파일이이 flatMap 동작에서 피해야 스트림을 생성하기 위해 패턴 문자열을 사용 MultiPatternSpliterator.matches(s, reTimeStamp, reHostName, reServiceTime)을 사용할 수 것이지만

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)와 같은 세 번째 패턴을 추가하고 같은 파일 java 프로세스에서 다시 실행하는 순간 메모리에 파일을 영원히 보관 (VisualVM을 통해 모니터링)하여 오류가 발생하지 않고 중단됩니다. 이것은 Stream.concat (regex1, regex2) - 작동 방식과 동일합니다. Stream.concat (regex1, regex2, regex3) - Java 프로세스가 중단됩니다. – Shan