web-dev-qa-db-ja.com

python-regex search and findall

特定の正規表現の文字列内のすべての一致を見つける必要があります。 findall()を使用して、期待どおりに動作しないケースに遭遇するまで、これを実行してきました。例えば:

_regex = re.compile('(\d+,?)+')
s = 'There are 9,000,000 bicycles in Beijing.'

print re.search(regex, s).group(0)
> 9,000,000

print re.findall(regex, s)
> ['000']
_

この場合、 search() は必要なもの(最長の一致)を返しますが、 findall() は動作が異なりますが、ドキュメントにはそのように示されています。同じである:

findall()は、search()のように最初のパターンだけでなく、パターンのすべての出現に一致します。

  • なぜ動作が違うのですか?

  • search()(または他の何か)でfindall()の結果を達成するにはどうすればよいですか?

15
armandino

わかりました、何が起こっているのかわかります...ドキュメントから:

パターンに1つ以上のグループが存在する場合は、グループのリストを返します。パターンに複数のグループがある場合、これはタプルのリストになります。

結局のところ、「(\ d +、?)」というグループがあります...したがって、返されるのは、このグループの最後のオカレンス、つまり000です。

1つの解決策は、このように正規表現全体をグループで囲むことです。

regex = re.compile('((\d+,?)+)')

次に、[( '9,000,000'、 '000')]を返します。これは、一致した両方のグループを含むタプルです。もちろん、あなたは最初のものだけを気にします。

個人的には、次の正規表現を使用します

regex = re.compile('((\d+,)*\d+)')

「これは悪い数字9,123です」のようなものの一致を避けるために

編集します。

式を括弧で囲んだり、タプルを処理したりする必要がないようにする方法は次のとおりです。

s = "..."
regex = re.compile('(\d+,?)+')
it = re.finditer(regex, s)

for match in it:
  print match.group(0)

finditerは、見つかったすべての一致にアクセスするために使用できるイテレータを返します。これらの一致オブジェクトはre.searchが返すものと同じであるため、group(0)は期待する結果を返します。

18
aleph_null

@aleph_nullの answer は、問題の原因を正しく説明していますが、より良い解決策があると思います。この正規表現を使用します。

_regex = re.compile(r'\d+(?:,\d+)*')
_

それが優れているいくつかの理由:

  1. _(?:...)_は非キャプチャグループであるため、一致ごとに1つの結果しか得られません。

  2. \d+(?:,\d+)*は、より優れた正規表現であり、より効率的で、誤検知を返す可能性が低くなります。

  3. 可能であれば、正規表現には常にPythonの生の文字列を使用する必要があります。文字列リテラルエスケープシーケンスとして解釈される正規表現エスケープシーケンス(単語境界の_\b_など)に驚かされる可能性は低くなります_\b_ forbackspace)のように。

7
Alan Moore