Python-Extraktmuster stimmt überein

128

Python 2.7.1 Ich versuche, den regulären Python-Ausdruck zu verwenden, um Wörter innerhalb eines Musters zu extrahieren

Ich habe eine Zeichenfolge, die so aussieht

someline abc
someother line
name my_user_name is valid
some more lines

Ich möchte das Wort "my_user_name" extrahieren. Ich mache so etwas

import re
s = #that big string
p = re.compile("name .* is valid", re.flags)
p.match(s) #this gives me <_sre.SRE_Match object at 0x026B6838>

Wie extrahiere ich jetzt meinen_Benutzernamen?

Kannan Ekanath
quelle

Antworten:

158

Sie müssen aus Regex erfassen. searchWenn das Muster gefunden wurde, rufen Sie die Zeichenfolge mit ab group(index). Vorausgesetzt, es werden gültige Prüfungen durchgeführt:

>>> p = re.compile("name (.*) is valid")
>>> result = p.search(s)
>>> result
<_sre.SRE_Match object at 0x10555e738>
>>> result.group(1)     # group(1) will return the 1st capture.
'my_user_name'
UltraInstinct
quelle
25
Bist du sicher, dass das nicht group(0)für das erste Spiel ist?
Sharshofski
33
Ein bisschen spät, aber sowohl ja als auch nein. group(0)Gibt den übereinstimmenden Text zurück, nicht die erste Erfassungsgruppe. Der Codekommentar ist korrekt, während Sie Capture-Gruppen und Übereinstimmungen zu verwirren scheinen. group(1)Gibt die erste Erfassungsgruppe zurück.
Andrewgu
1
Ich bekommeNameError: name '_' is not defined
Ian G
Ihre zweite Zeile sollte ich lesen _ = p.search(s). Ich sehe, dass erwähnt wird, dass das Ergebnis auf gesetzt wird, _aber der Code spiegelt dies nicht wider. Ich habe zu _ = p.search(s)dieser zweiten Zeile gewechselt und es funktioniert.
Ian G
2
@IanG Es tut mir leid, ich werde meine Antwort aktualisieren. Übrigens, mit einer Standard-Python-REPL wird das letzte Ergebnis in einer speziellen Variablen namens gespeichert _. Es ist nirgendwo anders gültig.
UltraInstinct
57

Sie können passende Gruppen verwenden:

p = re.compile('name (.*) is valid')

z.B

>>> import re
>>> p = re.compile('name (.*) is valid')
>>> s = """
... someline abc
... someother line
... name my_user_name is valid
... some more lines"""
>>> p.findall(s)
['my_user_name']

Hier verwende ich re.findalleher als re.searchalle Instanzen von zu bekommen my_user_name. Mit re.searchmüssen Sie die Daten aus der Gruppe für das Übereinstimmungsobjekt abrufen:

>>> p.search(s)   #gives a match object or None if no match is found
<_sre.SRE_Match object at 0xf5c60>
>>> p.search(s).group() #entire string that matched
'name my_user_name is valid'
>>> p.search(s).group(1) #first group that match in the string that matched
'my_user_name'

Wie in den Kommentaren erwähnt, möchten Sie Ihren Regex möglicherweise nicht gierig machen:

p = re.compile('name (.*?) is valid')

um nur das Zeug zwischen 'name 'und dem nächsten aufzunehmen ' is valid'(anstatt zuzulassen, dass Ihr Regex andere ' is valid'in Ihrer Gruppe aufnimmt .

mgilson
quelle
2
Es ist möglich, dass eine nicht gierige Übereinstimmung erforderlich ist ... (es sei denn, ein Benutzername kann aus mehreren Wörtern bestehen ...)
Jon Clements
@ JonClements - Du meinst (.*?)? Ja, das ist möglich, aber nicht notwendig, es sei denn, wir verwendenre.DOTALL
mgilson
Ja - wird re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')wahrscheinlich nicht die gewünschten Ergebnisse liefern ...
Jon Clements
Dies funktioniert nicht für Python 2.7.1? Es druckt nur ein Musterobjekt?
Kannan Ekanath
@CalmStorm - Welcher Teil funktioniert nicht (ich habe auf python2.7.3 getestet)? Der Teil, den ich benutze, .groupist genau der gleiche wie die Antwort, die Sie akzeptiert haben ...
mgilson
16

Sie könnten so etwas verwenden:

import re
s = #that big string
# the parenthesis create a group with what was matched
# and '\w' matches only alphanumeric charactes
p = re.compile("name +(\w+) +is valid", re.flags)
# use search(), so the match doesn't have to happen 
# at the beginning of "big string"
m = p.search(s)
# search() returns a Match object with information about what was matched
if m:
    name = m.group(1)
else:
    raise Exception('name not found')
Apalala
quelle
10

Vielleicht ist das etwas kürzer und leichter zu verstehen:

import re
text = '... someline abc... someother line... name my_user_name is valid.. some more lines'
>>> re.search('name (.*) is valid', text).group(1)
'my_user_name'
John
quelle
9

Sie möchten eine Erfassungsgruppe .

p = re.compile("name (.*) is valid", re.flags) # parentheses for capture groups
print p.match(s).groups() # This gives you a tuple of your matches.
Henry Keiter
quelle
9

Sie können Gruppen (mit '('und gekennzeichnet ')') verwenden, um Teile der Zeichenfolge zu erfassen. Die group()Methode des Übereinstimmungsobjekts gibt Ihnen dann den Inhalt der Gruppe:

>>> import re
>>> s = 'name my_user_name is valid'
>>> match = re.search('name (.*) is valid', s)
>>> match.group(0)  # the entire match
'name my_user_name is valid'
>>> match.group(1)  # the first parenthesized subgroup
'my_user_name'

In Python 3.6+ können Sie auch in ein Übereinstimmungsobjekt indizieren, anstatt Folgendes zu verwenden group():

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'
Eugene Yarmash
quelle
6

Hier ist eine Möglichkeit, dies ohne Verwendung von Gruppen zu tun (Python 3.6 oder höher):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'
Wolfskatzen
quelle
1
Dies befasst sich mit Python Regex, jedoch nicht mit der spezifischen Frage von OP.
Aleister Tanek Javas Mraz
Außerdem fügt dies den vorhandenen Antworten, die die Indizierungssyntax 3.6+ erwähnen, im Grunde nichts Neues hinzu.
Eugene Yarmash
3

Sie können auch eine Erfassungsgruppe verwenden (?P<user>pattern)und wie ein Wörterbuch auf die Gruppe zugreifen match['user'].

string = '''someline abc\n
            someother line\n
            name my_user_name is valid\n
            some more lines\n'''

pattern = r'name (?P<user>.*) is valid'
matches = re.search(pattern, str(string), re.DOTALL)
print(matches['user'])

# my_user_name
Ryan Stefan
quelle
1

Es scheint, als ob Sie tatsächlich versuchen, einen Namen zu extrahieren, indem Sie einfach eine Übereinstimmung finden. In diesem Fall ist es hilfreich, Span-Indizes für Ihre Übereinstimmung zu haben, und ich würde die Verwendung empfehlen re.finditer. Als Verknüpfung wissen Sie, dass der nameTeil Ihres regulären Ausdrucks Länge 5 und is validLänge 9 ist, sodass Sie den passenden Text in Scheiben schneiden können, um den Namen zu extrahieren.

Hinweis - In Ihrem Beispiel sieht es so aus, als wäre ses eine Zeichenfolge mit Zeilenumbrüchen.

## covert s to list of strings separated by line:
s2 = s.splitlines()

## find matches by line: 
for i, j in enumerate(s2):
    matches = re.finditer("name (.*) is valid", j)
    ## ignore lines without a match
    if matches:
        ## loop through match group elements
        for k in matches:
            ## get text
            match_txt = k.group(0)
            ## get line span
            match_span = k.span(0)
            ## extract username
            my_user_name = match_txt[5:-9]
            ## compare with original text
            print(f'Extracted Username: {my_user_name} - found on line {i}')
            print('Match Text:', match_txt)
Chiceman
quelle