Python提取模式匹配


129

Python 2.7.1我正在尝试使用python正则表达式来提取模式内的单词

我有一些看起来像这样的字符串

someline abc
someother line
name my_user_name is valid
some more lines

我要提取单词“ my_user_name”。我做类似的事情

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>

如何立即提取my_user_name?

Answers:


159

您需要从正则表达式捕获。search对于模式,如果找到,请使用检索字符串group(index)。假设执行了有效的检查:

>>> 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.
                        # group(0) will returned the entire matched text.
'my_user_name'

26
您确定不是group(0)第一次比赛吗?
sharshofski 2015年

33
有点晚了,但都是和不是。group(0)返回匹配的文本,而不是第一个捕获组。该代码注释是正确的,尽管您似乎混淆了捕获组和匹配项。group(1)返回第一个捕获组。
andrewgu

1
我得到NameError: name '_' is not defined
Ian G

我认为您的第二行应该阅读_ = p.search(s)。我看到它提到将结果设置为,_但是代码没有反映出来。我更改_ = p.search(s)为第二行,它可以工作。
伊恩·G

2
@IanG对不起,我将更新我的答案。顺便说一句,使用标准python REPL,最后的结果存储在名为的特殊变量中_。在其他任何地方都无效。
UltraInstinct

57

您可以使用匹配组:

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

例如

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

在这里,我使用re.findall而不是re.search获取的所有实例my_user_name。使用re.search,您需要从match对象上的组中获取数据:

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

如评论中所述,您可能希望使正则表达式不贪心:

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

只能提取到'name '下一个之间的内容' is valid'(而不是让您的正则表达式来提取' is valid'组中的其他内容。


2
可能需要非贪婪的匹配...(除非用户名可以是多个单词...)
乔恩·克莱门茨

@JonClements-你的意思是(.*?)?是的,这是可能的,尽管没有必要,除非OP我们使用re.DOTALL
mgilson 2013年

是的- re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')可能不会产生预期的结果...
乔恩·克莱门茨

这不适用于Python 2.7.1吗?它只是打印一个模式对象?
Kannan Ekanath 2013年

@CalmStorm-哪一部分不起作用(我在python2.7.3上进行了测试)?我使用的部分.group与您接受的答案完全相同...
mgilson

16

您可以使用如下形式:

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

10

也许这更短一些,更容易理解:

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'

9

您需要一个捕获组

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.

9

您可以使用组(用'('和表示')')捕获字符串的一部分。然后,match对象的group()方法为您提供组的内容:

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

在Python 3.6及更高版本中,您也可以索引到match对象中,而不是使用group()

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

6

这是一种无需使用组(Python 3.6或更高版本)的方法:

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'

1
这解决了Python Regex,但没有解决OP的特定问题。
Aleister Tanek Javas Mraz

此外,这基本上没有对提及3.6+索引语法的现有答案添加任何新内容。
Eugene Yarmash

3

您还可以使用捕获组(?P<user>pattern)并像字典一样访问该组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

1

看来您实际上是在尝试提取名称,而只是找到一个匹配项。在这种情况下,为您的比赛设置跨度索引会有所帮助,我建议您使用re.finditer。作为快捷方式,您知道name正则表达式的部分是长度5,而is valid长度是9,因此您可以对匹配的文本进行切片以提取名称。

注意-在您的示例中,它看起来像是s带有换行符的字符串,因此以下假设。

## 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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.