python pattern Alternativa a `match=re.match(); se partita:... 'idioma?



regex python search example (8)

Se vuoi controllare se qualcosa corrisponde a un'espressione regolare, in tal caso, stampa il primo gruppo, lo fai ..

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

Questo è completamente pedante, ma la variabile di match intermedia è un po 'fastidiosa ..

Lingue come Perl lo fanno creando nuove $1 .. $9 variabili per i gruppi di corrispondenza, come ..

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

Da questo commento reddit ,

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

..che pensavo fosse un'idea interessante, quindi ho scritto una semplice implementazione di questo:

#!/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))

(Questa funzionalità potrebbe teoricamente essere applicata all'oggetto _sre.SRE_Match )

Sarebbe bello se si potesse saltare l'esecuzione del blocco di codice dell'istruzione with , se non ci fosse una corrispondenza, il che potrebbe semplificarlo.

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

..ma questo sembra impossibile sulla base di ciò che posso dedurre da PEP 343

Qualche idea? Come ho già detto, questo è davvero banale fastidio, quasi al punto di essere codice-golf ..


Ecco la mia soluzione:

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

Puoi usare quante clausole elif se necessario.

Anche meglio:

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

Sia append e update restituiscono None . Quindi devi effettivamente controllare il risultato della tua espressione usando la parte o in ogni caso.

Sfortunatamente, questo funziona solo fino a quando il codice risiede al livello superiore, cioè non in una funzione.


Non è la soluzione perfetta, ma ti consente di concatenare diverse opzioni di corrispondenza per lo stesso 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"

Non penso sia banale. Non voglio dover cospargere un condizionale ridondante attorno al mio codice se sto scrivendo codice come quello spesso.

Questo è un po 'strano, ma puoi farlo con un iteratore:

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

La cosa strana è che sta usando un iteratore per qualcosa che non sta iterando - è più vicino a un condizionale, e a prima vista potrebbe sembrare che produrrà più risultati per ogni partita.

Sembra strano che un gestore di contesto non possa far saltare interamente la sua funzione gestita; mentre quello non è esplicitamente uno dei casi d'uso di "con", sembra un'estensione naturale.


Un'altra bella sintassi sarebbe qualcosa del genere:

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

Se stai facendo un sacco di questi in un posto, ecco una risposta alternativa:

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"

È possibile compilare la regex una volta con lo stesso thread safe di re, creare un singolo oggetto Matcher riutilizzabile per l'intera funzione e quindi è possibile utilizzarlo in modo molto conciso. Questo ha anche il vantaggio di poterlo invertire in modo ovvio - per farlo con un iteratore, devi passare una bandiera per dire di invertire il risultato.

Non è di grande aiuto se stai facendo una sola partita per funzione, però; non vuoi mantenere gli oggetti Matcher in un contesto più ampio di quello; causerebbe gli stessi problemi della soluzione di Blixt.


Ho un altro modo per farlo, basato sulla soluzione di Glen Maynard:

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

Simile alla soluzione di Glen, questo ittera 0 volte (se non corrisponde) o 1 (se corrisponde) volte.

Nessun sub necessario, ma meno ordinato di conseguenza.


Questo è ciò che faccio:

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

Cioè, passo una lista alla funzione per emulare il pass-by-reference.


Questo non è davvero carino, ma puoi approfittare della funzione getattr(object, name[, default]) usa in questo modo:

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

Per simulare il flusso del gruppo di stampa if match , è possibile (ab) utilizzare l'istruzione for questo modo:

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

Ovviamente puoi definire una piccola funzione per fare il lavoro sporco:

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




idioms