如何改进我的Python正则表达式语法?

时间:2010-03-29 08:45:05

标签: regex python

我是Python的新手,对正则表达式来说相当新。 (我没有Perl经验。)

我能够以一种有效的方式使用正则表达式,但我不确定我的代码是否特别是Pythonic或consise。

例如,如果我想读取文本文件并打印出直接出现在每行中'foo'和'bar'之间的文本(假设这发生在一行或零行一行),我会写以下内容:

fileList = open(inFile, 'r')
pattern = re.compile(r'(foo)(.*)(bar)')
for line in fileList:
    result = pattern.search(line)
    if (result != None):
        print result.groups()[1]

有更好的方法吗? if是必要的,以避免在groups()上调用None。但是我怀疑有一种更简洁的方法可以在有一个字符串时获取匹配的字符串,而不会在没有时抛出错误。

我不希望像Perl一样不可读。我只想以最普通和最简单的方式完成这项共同任务。

4 个答案:

答案 0 :(得分:3)

我认为没关系。

一些小问题: -

  • 您可以将result.groups()[x]替换为result.group(x+1)
  • 如果您不需要捕获foobar,请使用r'foo(.*)bar'
  • 如果您使用的是Python 2.5+,请尝试使用the with statement,这样即使出现异常,也可以正常关闭文件。

BTW,作为一个5线程(不是我推荐的):

import re
pattern = re.compile(r'foo(.*)bar')
with open(inFile, 'r') as fileList:
  searchResults = (pattern.search(line) for line in fileList)
  groups = (result.group(1) for result in searchResults if result is not None)
  print '\n'.join(groups)

答案 1 :(得分:1)

有两个技巧:第一个是re.finditer正则表达式函数(和方法)。 第二个是使用mmap模块。

从re.DOTALL的文档中,我们可以注意到.与换行符不匹配:

  

没有这个标志,'。'将匹配除换行符之外的任何内容。

因此,如果您在文件中的任何位置查找所有匹配项(例如使用f.read()读取字符串时),您可以假装每一行都是一个孤立的子字符串(注意:但它并不完全正确。你希望^和$断言以这种方式工作,使用re.MULTILINE)。现在,因为你注意到我们假设每行只有零次或一次出现,我们不必担心re.finditer()匹配比它应该更多(因为它会!)。所以马上就可以用finditer()来代替所有这些:

fileList = open(inFile, 'r')
pattern = re.compile(r'foo(.*)bar')
for result in pattern.finditer(fileList.read()):
    print result.groups(1)

虽然这不是真的。这里的问题是为方便起见,整个文件被读入内存。如果有一个方便的方法可以做到这一点,而不是打破更大的文件,这将是很好的。而且,有!输入mmap模块。

mmap允许您将文件视为一个字符串(一个可变字符串,不能少!),并且它不会将整个内容加载到内存中。它的长短不一,您可以使用以下代码:

fileList = open(inFile, 'r+b')
fileS = mmap.mmap(fileList.fileno(), 0)
pattern = re.compile(r'foo(.*)bar')
for result in pattern.finditer(fileS):
    print result.groups(1)

并且它的工作方式相同,但不会立即消耗整个文件(希望如此)。

答案 2 :(得分:0)

你不需要正则表达式。将你的字符串拆分为“bar”,迭代它们,找到“foo”,对“foo”进行拆分并将结果输出到右边。当然,您可以使用其他字符串操作,例如获取索引和内容。

>>> s="w1 w2 foo what i want bar w3 w4 foowhatiwantbar w5"
>>> for item in s.split("bar"):
...     if "foo" in item:
...         print item.split("foo")[1:]
...
[' what i want ']
['whatiwant']

答案 3 :(得分:0)

我有一些小建议:

  • 除非您确定foobar每行不超过一次,否则最好使用.*?代替.*
  • 如果您需要确保foobar只应与整个词匹配(而不是foonlyrebar),则应添加{{ 1}}围绕它们的锚点(\b等。)
  • 您可以使用环视功能仅匹配匹配本身(\bfoo\b),因此现在(?<=\bfoo\b).*?(?=\bbar\b)将包含匹配项。但那不是更具可读性:)