2016-01-14 2 views
11

とファイル内のパターンは、私が(ちょうど抜粋)のようなファイルを持って考える私は<code>name</code>の行を発見したときに<code>foobar</code>を取得したいのjava 8

name: 'foobar' 

を探します。

私の現在のアプローチはない良さそうに見えます

Pattern m = Pattern.compile("name: '(.+)'"); 
try (Stream<String> lines = Files.lines(ruleFile)) { 
    Optional<String> message = lines.filter(m.asPredicate()).findFirst(); 
    if (message.isPresent()) { 
     Matcher matcher = m.matcher(message.get()); 
     matcher.find(); 
     String group = matcher.group(1); 
     System.out.println(group); 
    } 
} 

です。パターンとマッチャーの過度の使用は間違っているようです。

もっと簡単な方法がありますか?特に複数のキーがある場合、このような検索をしたいのですか?

答えて

21

私は二回、パターンにマッチするのを避けるために、より多くのこのような何かを期待する:

Pattern p = Pattern.compile("name: '([^']*)'"); 
lines.map(p::matcher) 
    .filter(Matcher::matches) 
    .findFirst() 
    .ifPresent(matcher -> System.out.println(matcher.group(1))); 

つまり、各文字列の照合のために、最初のグループうち、1本の印刷のために、一致した最初のものを取得します。これは、すべての試合の流れを返すメソッドMatcher.results()を使用しています

Matcher m = Pattern.compile("name: '(.+)'").matcher(""); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> m.reset(line).results().limit(1)) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

+0

'map(p :: matcher)'パイプラインステージは、読み込まれた各行に対して新しい 'Matcher'オブジェクトを作成します。非常に大きなファイルの場合、これは効率の悪いソースになります。代わりに、再利用可能な 'Matcher'オブジェクトを作成することができます。' .map(matcher :: reset) 'は、ここで示すように、再利用可能な' matcher'に読み込まれた行からマップするために使用されます(https: /stackoverflow.com/a/47877960/3690024)。ストリームパイプラインでのステートフルオブジェクトの再利用はあらゆる種類のルールに違反しますので、大きなファイルを読んでいる場合のみこれを推奨し、ボトルネックと判断します。 – AJNeufeld

7

これは次のようにJavaの9ソリューションは、最も可能性の高いどのように見えるかです。 flatMapを介してストリームのストリームと一致のストリームを組み合わせることで、ファイルのすべての一致を処理することができます。元のコードは最初の行の一致を処理するだけなので、同じ行為を得るために各行の一致にlimit(1)を追加するだけです。

残念ながら、この機能は、Java 8に欠けている、しかし、今後のリリースに潜入すると、暫定的な解決策は、のように見えるかもしれどのようなアイデア取得できます:サブストリームの作成を簡素化するために

Matcher m = Pattern.compile("name: '(.+)'").matcher(""); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> m.reset(line).find()? Stream.of(m.toMatchResult()): null) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

を、このソリューションが利用最初の一致のみが意図され、最初に単一の要素ストリームが作成されることを意味します。

しかし、問題のパターン'name: '(.+)'と、我々が.+としてマッチの数を制限するかどうかは関係ありませんのでご注意を貪欲に、最後のフォローアップ'ラインのまでのすべての文字にマッチしますので、他の試合は不可能です。むしろ最後 1またはname: '([^']*)'と同じように、明示的に'をスキップして威嚇するより'まで消費name: '(.*?)'とのような消極的量指定子を使用している場合物事は異なっています。シングルスレッドの使用(これはこれまで、並列処理の恩恵を受けるにくい)でうまく動作用いる上記


ソリューション共有Matcher。あなたがスレッドセーフ側になりたい場合しかし、あなただけPatternを共有することができ、代わりにm.reset(line)を呼び出すMatcherを作成します。

Pattern pattern = Pattern.compile("name: '(.*)'"); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> pattern.matcher(line).results().limit(1)) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

RESPを。 Javaの場合8

try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> {Matcher m=pattern.matcher(line); 
          return m.find()? Stream.of(m.toMatchResult()): null;}) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

これは、ローカル変数の導入によって簡潔ではありません。これは、前map操作することで回避できますが、私たちがいる限り、この時点であるときに、ラインごとに単一の試合のために、我々は唯一のヘッドとして、我々はflatMapその後、必要はありません。各Matcherので

try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.map(pattern::matcher).filter(Matcher::find) 
     .forEach(m -> System.out.println(m.group(1))); 
} 

を一度だけ使用され、非干渉的な方法で、その変更可能な性質はここで害されず、不変のMatchResultへの変換は不要になる。することができ

それが今まで必要になった場合ただし、これらのソリューションは、行ごとに複数の一致を処理するためにスケーリングすることができない...

0

何度も何度も新しいMatcherオブジェクトを作成するには@khelwood結果によって答え、長いファイルがスキャンされた場合の非効率性の原因

次の解決策では、マッチャを1回だけ作成し、ファイルの各行に再利用します。

Pattern p = Pattern.compile("name: '([^']*)'"); 
Matcher matcher = p.matcher(""); // Create a matcher for the pattern 

Files.lines(ruleFile) 
    .map(matcher::reset)   // Reuse the matcher object 
    .filter(Matcher::matches) 
    .findFirst() 
    .ifPresent(m -> System.out.println(m.group(1))); 

警告 - 不審なハックを控え

.map(matcher::reset)パイプラインステージマジック/ハックが起こる場所です。効果的にmatcher.reset(line)を呼び出します。これはmatcherをリセットしてファイルから読み込んだ行の次の一致を実行し、自身を返して連鎖呼び出しを許可します。 .map(...)ストリーム演算子は、Matcherオブジェクトへのラインからのマッピングとしてこれを見ているが、実際には、我々は、このコースを

など、副作用に関する規則のあらゆる種類に違反し、同じオブジェクトmatcherたびにマッピングを維持しますをパラレルストリームに使用できませんが、幸いにもファイルからの読み込みは本質的に逐次的です。

ハッキングまたは最適化?私はアップ/ダウン投票が決定すると思います。

関連する問題

 関連する問題