特定の正規表現にあるキャプチャグループの数を決定する方法はありますか?
私は次のことができるようになりたいです:
def groups(regexp, s):
""" Returns the first result of re.findall, or an empty default
>>> groups(r'(\d)(\d)(\d)', '123')
('1', '2', '3')
>>> groups(r'(\d)(\d)(\d)', 'abc')
('', '', '')
"""
import re
m = re.search(regexp, s)
if m:
return m.groups()
return ('',) * num_of_groups(regexp)
これにより、次のようなことが可能になります。
first, last, phone = groups(r'(\w+) (\w+) ([\d\-]+)', 'John Doe 555-3456')
ただし、num_of_groups
の実装方法がわかりません。 (現在、私はそれを回避しています。)
EDIT:rsliteからのアドバイス に続いて、re.findall
をre.search
に置き換えました。
sre_parse
は、最も堅牢で包括的なソリューションのようですが、ツリートラバーサルが必要であり、少し重いようです。
MizardXの正規表現はすべてのベースをカバーしているようですので、それを使用します。
def num_groups(regex):
return re.compile(regex).groups
f_x = re.search(...)
len_groups = len(f_x.groups())
一致オブジェクトのlastindex
プロパティは、探しているものである必要があります。 re module docs を参照してください。
Sre_parseの中から何かが役立つかもしれません。
一見すると、次のようなものかもしれません。
>>> import sre_parse
>>> sre_parse.parse('(\d)\d(\d)')
[('subpattern', (1, [('in', [('category', 'category_digit')])])),
('in', [('category', 'category_digit')]),
('subpattern', (2, [('in', [('category', 'category_digit')])]))]
つまりタイプ「サブパターン」のアイテムを数える:
import sre_parse
def count_patterns(regex):
"""
>>> count_patterns('foo: \d')
0
>>> count_patterns('foo: (\d)')
1
>>> count_patterns('foo: (\d(\s))')
1
"""
parsed = sre_parse.parse(regex)
return len([token for token in parsed if token[0] == 'subpattern'])
ここではルートレベルのパターンのみをカウントしているため、最後の例では1しか返されないことに注意してください。これを変更するには、tokensを再帰的に検索する必要があります。
まず最初に、re.findallの最初の結果のみが必要な場合は、一致を返すre.searchまたはNoneを使用することをお勧めします。
グループ番号については、左括弧 '('を '\'でエスケープされたものを除いて)の数を数えることができます。そのために別の正規表現を使用できます:
def num_of_groups(regexp):
rg = re.compile(r'(?<!\\)\(')
return len(rg.findall(regexp))
これは、正規表現に非キャプチャグループが含まれている場合、および '('が '[(]'として使用することでエスケープされている場合は機能しません。したがって、これはあまり信頼できません。ただし、使用する正規表現によっては、助けて。
間違っているかもしれませんが、正規表現が一致した場合に返されるグループの数を見つける方法はないと思います。この作業を希望どおりに行うために私が考えることができる唯一の方法は、特定の正規表現が期待する一致の数を引数として渡すことです。
明確にするために:findallが成功した場合、最初の一致のみが返されるようにしますが、失敗した場合は空の文字列のリストが必要ですか?コメントはリストとして返されているすべての一致を示しているようです。
基礎としてコードを使用する:
def groups(regexp, s):
""" Returns the first result of re.findall, or an empty default
>>> groups(r'(\d)(\d)(\d)', '123')
('1', '2', '3')
>>> groups(r'(\d)(\d)(\d)', 'abc')
('', '', '')
"""
import re
m = re.search(regexp, s)
if m:
return m.groups()
return ('',) * len(m.groups())