Python中的类Perl正则表达式

11

在 Perl 中,我会像这样使用正则表达式来获取不同的字段,将不同的字段用 () 分隔,并使用 $ 获取它们。

foreach $line (@lines)
{
 $line =~ m/(.*?):([^-]*)-(.*)/;
  $field_1 = $1
  $field_2 = $2
  $field_3 = $3
}

我该如何在Python中实现这样的功能?

5个回答

21

您代码片段的“规范”Python翻译:

import re

myre = re.compile(r'(.*?):([^-]*)-(.*)')
for line in lines:
    mo = myre.search(line)
    field_1, field_2, field_3 = mo.groups()

导入re是必须的(通常在模块顶部进行导入,但这不是强制性的)。预编译RE是可选的(如果使用re.search函数,则会即时编译模式),但建议这样做(这样你就不会依赖于已编译的RE对象模块缓存以提高性能,另外还需要一个RE对象并调用其方法,这在Python中更为常见)。

您可以使用match方法(始终尝试从开头匹配,无论您的模式是否以'^'开头)或search方法(尝试匹配任何位置); 对于给定的模式,它们应该是等价的(但我不能100%确定)。

.groups()方法返回所有匹配组,因此您可以一次性将它们全部分配(在Python中使用列表,就像在Perl中使用数组一样,可能更正常,但由于您选择在Perl中使用标量,因此在Python中也可以采用相同的方式)。

如果任何行不匹配RE,则会引发异常,这样做可以知道所有行都匹配(我不确定您的Perl的行为是什么,但我认为它会“重用”上一个匹配行的值,这是奇怪的...除非,再次强调您知道所有行都匹配;-)。如果您只想跳过不匹配的行,请将最后一个语句更改为以下两个语句:

    if mo:
        field_1, field_2, field_3 = mo.groups()

13

在Perl中,使用数组比用数字后缀一堆标量要好得多。例如:

foreach my $line ( @lines ) { 
    my @matches = ( $line =~ m/(.*?):([^-]*)-(.*)/ );
    ...
}

在Python中,re模块返回一个包含捕获组信息的匹配对象。因此你可以这样写:

match = re.search( '(.*?):([^-]*)-(.*)', line )

那么你的匹配结果将会在 match.group(1)match.group(2) 等中得到。


8
Python支持使用re模块进行正则表达式。re.search()方法返回一个MatchObject,该对象具有像group()这样的方法,您可以使用它来检索“捕获组”信息。
例如:
m = re.search(r'(.*?):([^-]*)-(.*)', line)
field_1 = m.group(1)
field_2 = m.group(2)
field_3 = m.group(3)

6
而且不要忘记在Python中,TIMTOWTDI(有多种方法去做);)
import re
p = re.compile(r'(\d+)\.(\d+)')
num_parts = p.findall('11.22   333.444') # List of tuples.
print num_parts                          # [('11', '22'), ('333', '444')]

我认为你把Python和Perl搞混了。只需要读取import this(也就是Python之禅,或者直接运行python -c "import this" | grep -i there)就可以明白了。 - Aleksi Torhamo
@AleksiTorhamo 也许你把严肃和玩笑搞混了?;) - FMc
啊,好的 :-) 只是今天我第二次碰到有人这么说,所以我想我最好还是保持天真/信息量大的一面 :) (是的,我很确定另一个人是认真的 :D) - Aleksi Torhamo

6
作为另一个例子,Python 提供了非常好的支持命名捕获组(事实上,Python 开创了对命名捕获组的支持)。
要使用命名捕获组,只需在捕获组的左括号内添加?P<the_name_of_the_group>即可。
这使您能够非常容易地将所有匹配项放入字典中:
>>> import re
>>> x = re.search("name: (?P<name>\w+) age: (?P<age>\d+)", "name: Bob age: 20")
>>> x.groupdict()
{'age': '20', 'name': 'Bob'}

以下是原帖作者的示例,已修改为使用命名捕获组

import re

find_fields_regex = re.compile(r'(?P<field1>.*?):(?P<field2>[^-]*)-(?P<field3>.*)')
for line in lines:
    search_result = find_fields_regex.search(line)
    all_the_fields = search_result.groupdict()

现在,all_the_fields是一个字典,其中键对应于捕获组名称("field1"、"field2"和"field3"),值对应于各自捕获组的内容。 为什么你应该优先使用命名捕获组
  • 使用命名捕获组时,无论您修改正则表达式模式以添加更多捕获组还是删除现有捕获组,所有内容仍然会放入正确的键下的字典中。但是如果没有命名捕获组,则每次组数更改时都必须仔细检查变量分配。
  • 命名捕获组使您的捕获组具有自我记录功能。
  • 如果需要,仍然可以使用数字来引用这些组:
>>> import re
>>> x = re.search("name: (?P<name>\w+) age: (?P<age>\d+)", "name: Bob age: 20")
>>> x.groupdict()
{'age': '20', 'name': 'Bob'}
>>> x.group(1)
'Bob'
>>> x.group(2)
'20'

一些优秀的正则表达式资源:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接