31

TL; DR

MatcherのAPIの背後にある設計上の決定は何ですか?

バックグラウンド

Matcher私が予期していなかった振る舞いをしていて、正当な理由を見つけることができません。APIドキュメントには次のように書かれています。

作成されたマッチャーは、3種類の一致操作を実行するために使用できます。[...]これらの各メソッドは、成功または失敗を示すブール値を返します。一致の成功に関する詳細情報は、マッチャーの状態を照会することで取得できます。

APIドキュメントにさらに記載されているのは次のとおりです。

マッチャーの明示的な状態は、最初は未定義です。一致が成功する前にその一部を照会しようとすると、IllegalStateExceptionがスローされます。

String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
System.out.println(matcher.group("foo")); // (1)
System.out.println(matcher.group("bar"));

このコードは

java.lang.IllegalStateException: No match found

(1)。これを回避するには、を許可する状態にするmatches()か、または他のメソッドを呼び出す必要があります。次の作品:Matchergroup()

String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
matcher.matches(); // (2)
System.out.println(matcher.group("foo"));
System.out.println(matcher.group("bar"));

matches()atに呼び出しを追加すると、を呼び出す適切な状態に(2)設定されます。Matchergroup()

質問、おそらく建設的ではない

このAPIがこのように設計されているのはなぜですか?でビルドするときに自動的に一致しないのはなぜですか?MatcherPatter.matcher(String)

4

6 に答える 6

36

実際、あなたはドキュメントを誤解していました。あなたが引用した声明をもう一度見てください:-

一致が成功する前にその一部を照会しようとすると、IllegalStateExceptionがスローされます。

一致するものが見つからなかった場合、マッチャーはIllegalStateExceptionアクセスをスローする可能性があります。matcher.group()

したがって、実際にマッチングプロセスを開始するには、次のテストを使用する必要があります。

 - matcher.matches() //Or
 - matcher.find()

以下のコード:-

Matcher matcher = pattern.matcher();  

matcherインスタンスを作成するだけです。これは実際には文字列とは一致しません。試合が成功したとしても。したがって、一致が成功したかどうかを確認するには、次の条件を確認する必要があります。

if (matcher.matches()) {
    // Then use `matcher.group()`
}

そして、の条件がif返される場合false、それは何も一致しなかったことを意味します。したがって、matcher.group()この条件をチェックせずに使用するとIllegalStateException、一致するものが見つからなかった場合に取得されます。


Matcherあなたが言っているように設計されている場合、次のように呼び出すために、一致が見つかったかどうかを確認するためのチェックを行う必要があるとしますnullmatcher.group()

あなたがすべきだと思う方法:-

// Suppose this returned the matched string
Matcher matcher = pattern.matcher(s);  

// Need to check whether there was actually a match
if (matcher != null) {  // Prints only the first match

    System.out.println(matcher.group());
}

ただし、さらに一致するものを出力する場合は、パターンを文字列内で複数回一致させることができるため、次の一致を見つけるようにマッチャーに指示する方法が必要です。しかし、nullチェックはそれを行うことができません。そのためには、次の文字列と一致するようにマッチャーを前方に移動する必要があります。Matcherしたがって、目的を果たすためにクラスで定義されたさまざまなメソッドがあります。メソッドはmatcher.find()、すべての一致が見つかるまで文字列と一致します。

他の方法もありますがmatch、文字列の方法が異なります。これは、どのように照合するかによって異なります。したがって、最終的には、文字列に対してMatcher実行するクラスにmatchingなります。Patternクラスは、pattern照合するを作成するだけです。パターンに当てはまる場合Pattern.matcher()は、さまざまな方法で定義できるように、matchさまざまな方法を定義する方法が必要です。ですから、授業が必要になります。matchmatchingMatcher

だから、それが実際にある方法:-

Matcher matcher = pattern.matcher(s);

   // Finds all the matches until found by moving the `matcher` forward
while(matcher.find()) {
    System.out.println(matcher.group());
}

したがって、文字列に4つの一致が見つかった場合、最初の方法では最初の一致のみが印刷され、2番目の方法ではmatcher前方に移動して次のパターンに一致するようにすべての一致が印刷されます。

それが明確になることを願っています。

クラスのドキュメントには、Matcherクラスが提供する3つのメソッドの使用法が記載されています。

マッチャーは、パターンのマッチャーメソッドを呼び出すことによってパターンから作成されます。作成したマッチャーは、次の3種類の照合操作を実行するために使用できます。

  • matchesメソッドは、入力シーケンス全体をパターンと照合しようとします。

  • LookingAtメソッドは、最初から入力シーケンスをパターンと照合しようとします。

  • findメソッドは、入力シーケンスをスキャンして、パターンに一致する次のサブシーケンスを探します。

残念ながら、私は他の公式の情報源を見つけることができず、この問題の理由方法を明確に述べています。

于 2012-10-16T09:33:28.380 に答える
6

私の答えはRohitJainの答えと非常に似ていますが、「余分な」ステップが必要な理由がいくつか含まれています。

java.util.regexの実装

この線:

Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");

新しいPatternオブジェクトが割り当てられ、REを表す構造が内部に格納されます。文字、グループ、シーケンス、欲張りと非欲張り、繰り返しなどの情報が含まれます。

このパターンはステートレスで不変であるため、再利用でき、マルチヘッド可能であり、最適化されています。

台詞:

String s = "foo=23,bar=42";
Matcher matcher = p.matcher(s);

およびの新しいMatcherオブジェクトを返します-文字列をまだ読み取っていないオブジェクト。は実際には単なるステートマシンの状態であり、ステートマシンはです。PatternStringMatcherPattern

マッチングは、次のAPIを使用して、ステートマシンにマッチングプロセスをステップ実行することで実行できます。

  • lookingAt():入力シーケンスを最初からパターンと照合しようとします
  • find():入力シーケンスをスキャンして、パターンに一致する次のサブシーケンスを探します。

start()どちらの場合も、、、、end()およびgroup()メソッドを使用して中間状態を読み取ることができます。

このアプローチの利点

なぜ誰かが構文解析をステップスルーしたいと思うのでしょうか?

  1. 定量化が1より大きいグループ(つまり、繰り返して複数回一致するグループ)から値を取得します。たとえば、変数の割り当てを解析する以下の簡単なREでは、次のようになります。

    Pattern p = new Pattern("([a-z]=([0-9]+);)+");
    Matcher m = p.matcher("a=1;b=2;x=3;");
    m.matches();
    System.out.println(m.group(2)); // Only matches value for x ('3') - not the other values
    

    パターン上のJavaDocの「グループ化とキャプチャ」の「グループ名」のセクションを参照してください。

  2. 開発者はREをレクサーとして使用でき、開発者はレクサーされたトークンをパーサーにバインドできます。実際には、これは単純なドメイン言語では機能しますが、正規表現は、本格的なコンピューター言語を使用する方法ではない可能性があります。編集これは前の理由に部分的に関連していますが、最初にすべての入力を字句解析するよりも、テキストを処理する解析ツリーを作成する方が簡単で効率的であることがよくあります。
  3. (勇敢な人のために)REをデバッグして、どのサブシーケンスが一致しない(または誤って一致する)かを見つけることができます。

ただし、ほとんどの場合、ステートマシンでマッチングを実行する必要はないためmatches、パターンマッチングを実行して完了する便利なメソッド()があります。

于 2012-10-22T01:39:19.530 に答える
4

マッチャーが入力文字列を自動的に照合する場合、パターンを見つけたい場合にそれは無駄な労力になります。

matches()マッチャーを使用して、入力文字列のパターンをチェックできます。また、入力文字列find()のパターンに使用できます(一致するすべてのサブ文字列を繰り返し検索する場合でも)。これらの2つのメソッドのいずれかを呼び出すまで、マッチャーは実行するテストを認識しないため、一致するグループを提供することはできません。これらのメソッドのいずれかを呼び出しても、呼び出しが失敗する可能性があります(パターンが見つかりません)。その場合、toの呼び出しもgroup失敗する必要があります。

于 2012-10-20T08:17:08.900 に答える
2

これは予想され、文書化されています。

その理由は.matches()、一致があったかどうかを示すブール値を返すためです。一致した場合は、.group(...)意味のある呼び出しを行うことができます。それ以外の場合、一致するものがない場合、への呼び出し.group(...)は意味がありません。.group(...)したがって、を呼び出す前に呼び出すことを許可しないでくださいmatches()

マッチャーを使用する正しい方法は、次のようなものです。

Matcher m = p.matcher(s);
if (m.matches()) {
  ...println(matcher.group("foo"));
  ...
}
于 2012-10-16T09:30:47.637 に答える
2

私の推測では、設計上の決定は、存在と一致プロパティを混同しない、明確で明確に定義されたセマンティクスを持つクエリに基づいていたと思います。

これを考慮してください:マッチャーが何かに正常に一致しなかった場合、マッチャークエリは何を返すと思いますか?

まず考えてみましょうgroup()。何かが正常に一致しなかった場合、Matcherは空の文字列と一致していないため、空の文字列を返さないはずです。nullこの時点で戻ることができます。

start()では、考えてみましょうend()。各リターンint。この場合、どのようintな値が有効になりますか?確かに正の数はありません。どのような負の数が適切でしょうか?-1?

これらすべてを考慮すると、ユーザーは、一致が発生したかどうかを確認するために、すべてのクエリの戻り値をチェックする必要があります。または、完全に一致するかどうかを確認し、成功した場合、クエリセマンティクスはすべて明確に定義された意味を持ちます。そうでない場合、ユーザーはどの角度が照会されても一貫した動作を取得します。

再利用してIllegalStateExceptionも、エラー状態の最良の説明が得られなかった可能性があることを認めます。しかし、名前を変更/サブクラス化IllegalStateExceptionするNoSuccessfulMatchException場合、現在の設計がクエリの一貫性をどのように適用し、質問時に定義されていることがわかっているセマンティクスを持つクエリを使用するようにユーザーに促すかを理解できるはずです。

TL; DR:生物の特定の死因を尋ねることの価値は何ですか?

于 2012-10-19T18:43:54.017 に答える
1

の戻り値を確認する必要がありますmatcher.matches()true一致するものが見つかった場合は戻ります。それ以外の場合は戻りfalseます。

if (matcher.matches()) {
    System.out.println(matcher.group("foo"));
    System.out.println(matcher.group("bar"));
}

matcher.matches()一致するものが見つからずに電話をかけた場合matcher.group(...)でも、が表示されますIllegalStateException。それはまさにドキュメントが言っていることです:

マッチャーの明示的な状態は、最初は未定義です。一致が成功する前にその一部を照会しようとすると、IllegalStateExceptionがスローされます。

matcher.match()を返す場合false、成功した一致は見つかりませんでした。たとえば、を呼び出して一致に関する情報を取得することはあまり意味がありませんgroup()

于 2012-10-16T09:31:06.153 に答える