web-dev-qa-db-ja.com

`match = re.match();の代替。一致する場合:... `イディオム?

何かが正規表現に一致するかどうかを確認したい場合は、一致する場合は、最初のグループを印刷します。

import re
match = re.match("(\d+)g", "123g")
if match is not None:
    print match.group(1)

これは完全に衒学的ですが、中間のmatch変数は少し面倒です。

Perlのような言語は、..のようなマッチグループ用の新しい$1 ..$9変数を作成することによってこれを行います。

if($blah ~= /(\d+)g/){
    print $1
}

から このredditコメント

with re_context.match('^blah', s) as match:
    if match:
        ...
    else:
        ...

..これは面白いアイデアだと思ったので、簡単な実装を作成しました。

#!/usr/bin/env python2.6
import re

class SRE_Match_Wrapper:
    def __init__(self, match):
        self.match = match

    def __exit__(self, type, value, tb):
        pass

    def __enter__(self):
        return self.match

    def __getattr__(self, name):
        if name == "__exit__":
            return self.__exit__
        Elif name == "__enter__":
            return self.__name__
        else:
            return getattr(self.match, name)

def rematch(pattern, inp):
    matcher = re.compile(pattern)
    x = SRE_Match_Wrapper(matcher.match(inp))
    return x
    return match

if __name__ == '__main__':
    # Example:
    with rematch("(\d+)g", "123g") as m:
        if m:
            print(m.group(1))

    with rematch("(\d+)g", "123") as m:
        if m:
            print(m.group(1))

(この機能は、理論的には_sre.SRE_Matchオブジェクトにパッチを適用できます)

一致するものがなかった場合、withステートメントのコードブロックの実行をスキップできれば、これは簡単になります。

with rematch("(\d+)g", "123") as m:
    print(m.group(1)) # only executed if the match occurred

..しかし、これは私が推測できることに基づいて不可能に思えます PEP 34

何か案は?私が言ったように、これは本当に些細な煩わしさであり、ほとんどコードゴルフになるほどで​​す。

33
dbr

_Python 3.8_を開始し、 代入式(PEP 572) (_:=_演算子)を導入すると、条件値re.match(r'(\d+)g', '123g')を変数に取り込むことができるようになります。 matchNoneでないかどうかを確認し、条件の本体内で再利用するには、次のようにします。

_>>> if match := re.match(r'(\d+)g', '123g'):
...   print(match.group(1))
... 
123
>>> if match := re.match(r'(\d+)g', 'dddf'):
...   print(match.group(1))
...
>>>
_
2
Xavier Guihot

些細なことではないと思います。そのようなコードを頻繁に書いているのであれば、コードの周りに冗長な条件を散りばめる必要はありません。

これは少し奇妙ですが、イテレータを使用してこれを行うことができます。

import re

def rematch(pattern, inp):
    matcher = re.compile(pattern)
    matches = matcher.match(inp)
    if matches:
        yield matches

if __name__ == '__main__':
    for m in rematch("(\d+)g", "123g"):
        print(m.group(1))

奇妙なことに、反復していないものにイテレータを使用しています。条件に近く、一見すると、一致ごとに複数の結果が得られるように見える場合があります。

コンテキストマネージャがその管理対象関数を完全にスキップさせることができないのは奇妙に思えます。これは明示的に「with」のユースケースの1つではありませんが、自然な拡張のようです。

12
Glenn Maynard

もう1つのNice構文は、次のようになります。

header = re.compile('(.*?) = (.*?)$')
footer = re.compile('(.*?): (.*?)$')

if header.match(line) as m:
    key, value = m.group(1,2)
Elif footer.match(line) as m
    key, value = m.group(1,2)
else:
    key, value = None, None
4
mhubig

グレンメイナードの解決策に基づいて、これを行う別の方法があります:

for match in [m for m in [re.match(pattern,key)] if m]:
    print "It matched: %s" % match

グレンのソリューションと同様に、これは0(一致しない場合)または1(一致する場合)のいずれかを繰り返します。

サブは必要ありませんが、結果として整頓されていません。

1
AMADANON Inc.

これらの多くを1つの場所で行っている場合は、別の答えがあります。

import re
class Matcher(object):
    def __init__(self):
        self.matches = None
    def set(self, matches):
        self.matches = matches
    def __getattr__(self, name):
        return getattr(self.matches, name)

class re2(object):
    def __init__(self, expr):
        self.re = re.compile(expr)

    def match(self, matcher, s):
        matches = self.re.match(s)
        matcher.set(matches)
        return matches

pattern = re2("(\d+)g")
m = Matcher()
if pattern.match(m, "123g"):
    print(m.group(1))
if not pattern.match(m, "x123g"):
    print "no match"

Reと同じスレッドセーフで正規表現を1回コンパイルし、関数全体に対して単一の再利用可能なMatcherオブジェクトを作成すると、非常に簡潔に使用できます。これには、明らかな方法で逆にすることができるという利点もあります。イテレータを使用してそれを行うには、結果を反転するように指示するフラグを渡す必要があります。

ただし、関数ごとに1つの一致のみを実行している場合は、あまり役に立ちません。それよりも広いコンテキストでMatcherオブジェクトを保持する必要はありません。 Blixtのソリューションと同じ問題が発生します。

0
Glenn Maynard

完璧な解決策ではありませんが、同じstrに対して複数の一致オプションを連鎖させることができます。

class MatchWrapper(object):
  def __init__(self):
    self._matcher = None

  def wrap(self, matcher):
    self._matcher = matcher

  def __getattr__(self, attr):
    return getattr(self._matcher, attr)

def match(pattern, s, matcher):
  m = re.match(pattern, s)
  if m:
    matcher.wrap(m)
    return True
  else:
    return False

matcher = MatchWrapper()
s = "123g";
if _match("(\d+)g", line, matcher):
  print matcher.group(1)
Elif _match("(\w+)g", line, matcher):
  print matcher.group(1)
else:
  print "no match"
0
oneself

これは実際には見栄えがよくありませんが、次のように使用すると、getattr(object, name[, default])組み込み関数から利益を得ることができます。

>>> getattr(re.match("(\d+)g", "123g"), 'group', lambda n:'')(1)
'123'
>>> getattr(re.match("(\d+)g", "X23g"), 'group', lambda n:'')(1)
''

印刷グループに一致する場合フローを模倣するには、次のようにforステートメントを(ab)使用できます。

>>> for group in filter(None, [getattr(re.match("(\d+)g", "123g"), 'group', None)]):
        print(group(1))
123
>>> for group in filter(None, [getattr(re.match("(\d+)g", "X23g"), 'group', None)]):
        print(group(1))
>>> 

もちろん、汚い仕事をするための小さな関数を定義することができます:

>>> matchgroup = lambda p,s: filter(None, [getattr(re.match(p, s), 'group', None)])
>>> for group in matchgroup("(\d+)g", "123g"):
        print(group(1))
123
>>> for group in matchgroup("(\d+)g", "X23g"):
        print(group(1))
>>> 
0
etuardu

この場合、withを使用することが解決策ではないと思います。 BLOCK部分(ユーザーが指定)で例外を発生させ、__exit__メソッドがTrueを返して例外を「飲み込む」必要があります。だからそれは決して良く見えないでしょう。

Perl構文に似た構文を選択することをお勧めします。独自の拡張reモジュール(これをrexと呼びます)を作成し、モジュール名前空間に変数を設定します。

if rex.match('(\d+)g', '123g'):
    print rex._1

以下のコメントでわかるように、このメソッドはスコープセーフでもスレッドセーフでもありません。これを使用するのは、アプリケーションが将来マルチスレッド化されないこと、およびこれを使用しているスコープから呼び出された関数がalso同じ方法を使用します。

0
Blixt

これが私の解決策です:

import re

s = 'hello world'

match = []
if match.append(re.match('w\w+', s)) or any(match):
    print('W:', match.pop().group(0))
Elif match.append(re.match('h\w+', s)) or any(match):
    print('H:', match.pop().group(0))
else:
    print('No match found')

Elif句は必要な数だけ使用できます。

さらに良い:

import re

s = 'hello world'

if vars().update(match=re.match('w\w+', s)) or match:
    print('W:', match.group(0))
Elif vars().update(match=re.match('h\w+', s)) or match:
    print('H:', match.group(0))
else:
    print('No match found')

appendpdate return Noneの両方。したがって、すべての場合にまたは部分を使用して、式の結果を実際に確認する必要があります。

残念ながら、これはコードがトップレベルにある場合、つまり関数内にない場合にのみ機能します。

0
dennis

これが私がすることです:

def re_match_cond (match_ref, regex, text):
    match = regex.match (text)
    del match_ref[:]
    match_ref.append (match)
    return match

if __name__ == '__main__':
    match_ref = []
    if re_match_cond (match_ref, regex_1, text):
        match = match_ref[0]
        ### ...
    Elif re_match_cond (match_ref, regex_2, text):
        match = match_ref[0]
        ### ...
    Elif re_match_cond (match_ref, regex_3, text):
        match = match_ref[0]
        ### ...
    else:
        ### no match
        ### ...

つまり、リストを関数に渡して、参照渡しをエミュレートします。

0
Rhubbarb