10

動機

私は住所を解析しており、住所と国を別々の一致で取得する必要がありますが、国にはエイリアスがある可能性があります。

UK == United Kingdom, 
US == USA == United States,
Korea == South Korea, 

等々...

説明

したがって、私が行うことは、次のように、OR 演算子で区切られたすべての可能な国名 (少なくとも表示される可能性が高いもの) を持つ大きな正規表現を作成することです。

germany|us|france|chile

しかし問題は、次のような複数の単語の国名とその短いバージョンにあります。

Republic of MoldovaMoldova

これを例として使用すると、次の文字列が得られます。

'Somewhere in Moldova, bla bla, 12313, Republic of Moldova'

これから得たいもの:

'Somewhere in Moldova, bla bla, more bla, 12313'
'Republic of Moldova'

しかし、これは私が得るものです:

'Somewhere in Moldova, bla bla, 12313, Republic of'
'Moldova'

正規表現

いくつかのケースがあるので、私がこれまでに使用しているものは次のとおりです。

^(.*),? \(?(republic of moldova|moldova)\)?(.*[\d\-]+.*|,.*[:/].*)?$

国名の後にファックス、電話番号、郵便番号などがあるかもしれないので (私は気にしません)、最後に一致したグループを使用してそれらを削除します。

(.*[\d\-]+.*|,.*[:/].*)?

また、国名が括弧で囲まれている場合があるため、2 番目の一致グループ\(?\)?前後に、すべての国が含まれています。

(republic of moldova|moldova|...)

質問

問題は、大きいエントリのサブセットであるエントリがある場合、長いエントリよりも短いエントリが選択され、残りは base_address 文字列にとどまることです。2 つの値が一致する場合に、可能な最大の一致を選択するように正規表現に指示する方法はありますか?

編集

  1. 組み込みのreモジュールでPythonを使用しています
  2. m.buettner が示唆するように、最初に一致するグループを から(.*)に変更(.*?)すると、現在の問題は実際に修正されますが、別の問題も作成されます。他の例を考えてみましょう:

    'Department of Chemistry, National University of Singapore, 4512436 Singapore'

一致:

'Department of Chemistry, National University of'
'Singapore'

ここでは、一致するのが早すぎます。

4

2 に答える 2

6

あなたの問題は貪欲です。

最初の.*右は、できるだけ一致させようとします。それは文字列の終わりまでのすべてです。しかし、パターンの残りの部分は失敗します。そのため、エンジンはバックトラックし、最後に一致した文字を破棄して.*、残りのパターンを再試行します (それでも失敗します)。エンジンは、最終的に残りのパターンと一致するまで、このプロセスを繰り返します (一致に失敗し、1 文字をバックトラック/破棄し、再試行します)。これが最初に発生するのは、 が.*までのすべてに一致するときですMoldova(したがって.*、まだ消費しているRepublic of)。そして、代替 (これはまだ一致できませんrepublic of moldova) は喜んで一致moldovaし、結果としてそれを返します。

最も簡単な解決策は、繰り返しを非貪欲にすることです。

^(.*?)...

量指定子の直後の疑問符は「オプション」を意味するのではなく、「貪欲でない」ことを意味することに注意してください。これは単純に動作を逆にします。エンジンは最初に を完全に除外しようとし.*、バックトラックの過程でパターンの残りの部分との一致に失敗するたびに 1 文字追加します。

編集:

通常、貪欲に代わるより良い方法があります。コメントで述べたように、貪欲でない解決策は、文字列の前の部分の国が一致する可能性があるという別の問題をもたらします。代わりにできることは、国の前後に単語文字 (文字、数字、アンダースコア) がないことを確認するルックアラウンドを使用することです。つまり、国の単語は、カンマで囲まれているか、文字列のいずれかの末尾にある場合にのみ一致します。

^(.*),?(?<!\w)[ ][(]?(c|o|u|n|t|r|i|e|s)[)]?(?![ ]*\w)(.*[\d\-]+.*|,.*[:/].*)?$

ルックアラウンドは実際には一致の一部ではないため、パターンの残りの部分に干渉することはありません。一致の特定の位置で条件をチェックするだけです。私が追加した 2 つのルックアラウンドは、次のことを保証します。

  1. 国に先行する必須のスペースの前に、単語の文字はありません。
  2. スペースだけで区切られた国の後に単語文字はありません。

文字クラスとリテラルの括弧でスペースを (エスケープする代わりに) ラップしたことに注意してください。どちらも必須ではありませんが、私は読みやすさの観点からこれらを好むので、単なる提案にすぎません。

編集2:

abarnert がコメントで述べたように、正規表現のみのソリューションを使用しないのはどうですか?

で文字列を分割し、,すべての結果をトリミングして、これらを国のリストと照合することができます (おそらく正規表現を使用)。住所の構成要素がいずれかの国と同じである場合は、それを返すことができます。複数のものがあれば、少なくともあいまいさを検出して適切に処理できます。

于 2013-05-18T00:18:19.423 に答える