2010-03-29 45 views
3

我对Python很新,对于正则表达式来说还是比较新的。 (我没有Perl的经验。)如何提高我的Python正则表达式语法?

我能够使用正则表达式的方式,但我不确定我的代码是特别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以避免在None上拨打groups()。但我怀疑有一个更简洁的方式来获得匹配的字符串,当没有错误时不会抛出错误。

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

回答

3

我觉得很好。

一些小点: -

  • 您可以result.group(x+1)取代result.groups()[x]
  • 如果您不需要捕获foobar,只需使用r'foo(.*)bar'即可。
  • 如果您使用的是Python 2.5+,请尝试使用the with statement,这样即使有异常情况下文件可以正常关闭。

顺便说一句,作为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) 
+0

出于某种原因,'result.group(1)'为我捕获'foo',但'result.group(2)'工作。 – 2010-03-29 10:12:16

+0

@FarmBoy:因为你用'(foo)(。*)(bar)'而不是'foo(。*)bar'匹配。 – kennytm 2010-03-29 12:46:15

+0

元组索引不是基于0的吗?我期待'result.group(0)'会在我的代码中返回'foo'。 – 2010-03-29 16:01:35

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

和它的作用是相同的,但没有一次(希望)消耗整个文件。

0

我有一些小建议:

  • 除非你确信foobar可发生不超过每行一次,它的更好,如果你需要使用的.*?代替.*
  • 确保foobar只应作为整个单词匹配(而不是foonlyrebar),你应该添加他们(\bfoo\b等)
  • 周围 \b
  • 您可以使用lookaround来仅匹配匹配本身((?<=\bfoo\b).*?(?=\bbar\b)),所以现在result.group(0)将包含匹配项。但这不是更可读:)