بديل للمطابقة = RE.Match ()؛ إذا تطابق: ... `المصطلح؟

StackOverflow https://stackoverflow.com/questions/1152385

  •  18-09-2019
  •  | 
  •  

سؤال

إذا كنت ترغب في التحقق مما إذا كان هناك شيء يطابق Regex، إذا كان الأمر كذلك، فقم بطباعة المجموعة الأولى، أنت تفعل ..

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

.. لكن هذا يبدو مستحيلا بناء على ما يمكنني استنتاج من بيب 343.

أيه أفكار؟ كما قلت، هذا إزعاج تافهة حقا، تقريبا إلى حد كون الجولف ..

هل كانت مفيدة؟

المحلول

بدءا Python 3.8, ، وإدخال تعبيرات التعيين (PEP 572) (:= المشغل)، يمكننا الآن التقاط قيمة الشرط re.match(r'(\d+)g', '123g') في متغير match من أجل كل من الشيك إذا لم يكن كذلك None ثم أعد استخدامها داخل جسم الحالة:

>>> 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))
...
>>>

نصائح أخرى

لا أعتقد أنها تافهة. لا أريد أن أضع مع رش مشروط زائدة عن الزيادة حول التعليمات البرمجية إذا أكتب رمز مثل هذا في كثير من الأحيان.

هذا غريب بعض الشيء، ولكن يمكنك القيام بذلك مع جهاز كمتكر

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))

الشيء الغريب هو أنه يستخدم جهاز كمتقل لشيء لا يتنكر - إنه أقرب إلى مشروط، واللقاء الأول قد يبدو أنه سيؤدي إلى تحقيق نتائج متعددة لكل مباراة.

يبدو من الغريب أن مدير السياق لا يستطيع تخطي وظيفته المدارة بالكامل؛ في حين أن هذا ليس أحد صريحا من حالات استخدام "مع"، إلا أنه يبدو وكأنه تمديد طبيعي.

بناء جملة لطيف آخر سيكون مثل هذا:

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

لدي طريقة أخرى للقيام بذلك، بناء على محلول Glen Maynard:

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

على غرار حل Glen، فإن هذا إما 0 (إذا لم يكن هناك تطابق) أو 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"

يمكنك ترجمة Regex مرة واحدة مع نفس سلامة الخيط مثل إعادة، وإنشاء كائن Matcher واحد قابل لإعادة الاستخدام لهذه الوظيفة بأكملها، ثم يمكنك استخدامه بإيجاز للغاية. تتمتع ذلك أيضا بالفائدة التي يمكنك عكسها بطريقة واضحة - للقيام بذلك مع جهاز كمتكر فيه، كنت بحاجة إلى اجتياز العلم لتخبره عن عكس النتيجة.

لا يساعد الكثير إذا كنت تقوم بمباراة واحدة فقط لكل وظيفة، على الرغم من ذلك؛ أنت لا ترغب في الحفاظ على كائنات Matcher في سياق أوسع من ذلك؛ لأنه سيؤدي إلى نفس المشكلات مثل حل Blott.

لا أعتقد ذلك with هو الحل في هذه الحالة. يجب عليك رفع استثناء في BLOCK جزء (الذي يحدده المستخدم) ولديك __exit__ طريقة العودة True إلى "ابتلاع" الاستثناء. لذلك لن تبدو جيدة.

أقترح الذهاب إلى بناء جملة مماثل لبوليات بيرل. اصنع امتدت re الوحدة النمطية (سأتصل به rex) ولديها تحدد المتغيرات في مساحة الاسم الوحدة النمطية:

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

كما ترون في التعليقات أدناه، هذه الطريقة ليست نطاقا أو آمنا للخيط. ستستخدم هذا فقط إذا كنت متأكدا تماما من أن تطبيقك لن يصبح متعدد الخيوط في المستقبل وأن أي وظائف تسمى من النطاق الذي تستخدمه في الإرادة أيضا استخدم نفس الطريقة.

هذا ليس رائعا حقا، ولكن يمكنك الاستفادة من 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)
''

لتقليد إذا تطابق مجموعة الطباعة التدفق، يمكنك (AB) استخدام for بيان بهذه الطريقة:

>>> 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))
>>> 

ليس الحل المثالي، ولكنه يسمح لك بسلسلة خيارات مباراة متعددة لنفس 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"

إليك حلاي:

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')

يمكنك استخدام أكبر عدد ممكن أليف البنود حسب الحاجة.

حتى أفضل:

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')

كلاهما ألحق و تحديث إرجاع لا أحد. وبعد لذلك عليك أن تحقق فعلا نتيجة تعبيرك باستخدام أو جزء في كل حالة.

لسوء الحظ، هذا يعمل فقط طالما أن الرمز يقيم المستوى الأعلى، أي ليس في وظيفة.

هذا ما افعله:

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
        ### ...

وهذا هو، وأرمر قائمة إلى الوظيفة لمحاكاة مرجع المرور.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top