1

I am trying to learn some regular expressions in Python. The following does not produce the output I expected:

with open('ex06-11.html') as f:
    a = re.findall("<div[^>]*id\\s*=\\s*([\"\'])header\\1[^>]*>(.*?)</div>", f.read())
    # output: [('"', 'Some random text')]

The output I was expecting (same code, but without the backreference):

with open('ex06-11.html') as f:
    print re.findall("<div[^>]*id\\s*=\\s*[\"\']header[\"\'][^>]*>(.*?)</div>", f.read())
    # output: ['Some random text']

The question really boils down to: why is there a quotation mark in my first output, but not in my second? I thought that ([abc]) ... //1 == [abc] ... [abc]. Am I incorrect?

4

3 に答える 3

7

ドキュメントからre.findall

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

?:一致全体を返す場合は、キャプチャ グループを削除するか、開始括弧の後に追加して非キャプチャ グループに変更します。たとえば(foo)、正規表現を に変更します(?:foo)

もちろん、この場合、後方参照用のキャプチャ グループが必要なので、現在の正規表現を保持し、リスト内包表記を使用しre.finditer()て 2 番目のグループのみのリストを取得することをお勧めします。

regex = re.compile(r"""<div[^>]*id\s*=\s*(["'])header\1[^>]*>(.*?)</div>""")
with open('ex06-11.html') as f:
    a = [m.group(2) for m in regex.finditer(f.read())

補足として、正規表現の代わりに BeautifulSoup のような HTML パーサーを使用することを検討する必要があります。また、文字列内に一重引用符または二重引用符を含める必要がある場合は、三重引用符で囲まれた文字列を使用し、バックスラッシュをエスケープする必要がないように、正規表現を記述するときに生の文字列リテラルを使用する必要があります。

于 2013-09-25T18:44:35.327 に答える
4

動作は明確に文書化されています。を参照してくださいfindall:

文字列のリストとして、文字列内のパターンの重複しない一致をすべて返します。文字列は左から右にスキャンされ、見つかった順序で一致が返されます。

パターンに 1 つ以上のグループが存在する場合は、グループのリストを返します。パターンに複数のグループがある場合、これはタプルのリストになります。空の一致は、別の一致の先頭に触れない限り、結果に含まれます。

したがって、正規表現パターンにキャプチャ グループがある場合、findallメソッドは、特定の一致でキャプチャされたすべてのグループとgroup(0).

したがって、2番目のケースのように、非キャプチャグループを(?:[\"\'])使用するか、グループをまったく使用しないでください。

PS:バックスラッシュのエスケープを避けるために、正規表現パターンには生の文字列リテラルを使用してください。また、ループの外側で正規表現をコンパイルして、反復ごとに再コンパイルされないようにします。そのために使用re.compileします。

于 2013-09-25T18:44:20.567 に答える
0

この質問をしたとき、私は正規表現から始めたばかりでした。それ以来、私はドキュメントを完全に読んでおり、発見したことを共有したかっただけです.

まず、RohitFJが提案したことは、生の文字列を使用して (正規表現を読みやすくし、エラーを起こしにくくするため)、事前に を使用して正規表現をコンパイルしますre.compile。ID が「header」の HTML 文字列に一致させるには:

s = "<div id='header'>Some random text</div>"

次のような正規表現が必要です。

p = re.compile(r'<div[^>]*id\s*=\s*([\"\'])header\1[^>]*>(.*?)</div>')

正規表現の Python 実装では、キャプチャ グループは、正規表現の一部を括弧で囲むことによって作成されます(...)。キャプチャ グループは、一致するテキストの範囲をキャプチャします。これらは後方参照にも必要です。したがって、上記の正規表現では、2 つのキャプチャ グループがあります:([\"\'])(.*?). 最初のものは後方参照を可能にするために必要です\1。ただし、後方参照の使用 (およびそれらがキャプチャ グループを参照するという事実) には結果があります。この質問に対する他の回答で指摘されているように、findallon my patternpを使用するfindallと、すべてのグループから一致が返され、タプルのリストに入れられます。

print p.findall(s)
# [("'", 'Some random text')]

HTML タグ間のプレーン テキストのみが必要なので、これは探している出力ではありません。

(ほぼ間違いなく、以下を使用できます。

print p.findall(s)[0][1]
# Some random text

しかし、これは少し不自然かもしれません。)

したがって、(2 番目のグループによってキャプチャされた) HTML タグ間のテキストのみを返すために、次のgroup()メソッドを使用しp.search()ます。

print p.search(s).group(2)
# Some random text

最も単純な HTML 以外はすべて正規表現で処理すべきではなく、代わりにパーサーを使用する必要があることを十分に認識しています。しかし、これは Python での正規表現の基本を理解するためのチュートリアルの例にすぎません。

于 2013-10-09T09:23:05.870 に答える