使用正则表达式在Python中查找和捕获字符
在我解决一个来自 Python Challenge 的问题时,我尝试这样做:
我需要从一个文本文件中读取内容,文件里的字符如下:
DQheAbsaMLjTmAOKmNsLziVMenFxQdATQIjItwtyCHyeMwQTNxbbLXWZnGmDqHhXnLHfEyvzxMhSXzd
BEBaxeaPgQPttvqRvxHPEOUtIsttPDeeuGFgmDkKQcEYjuSuiGROGfYpzkQgvcCDBKrcYwHFlvPzDMEk
MyuPxvGtgSvWgrybKOnbEGhqHUXHhnyjFwSfTfaiWtAOMBZEScsOSumwPssjCPlLbLsPIGffDLpZzMKz
jarrjufhgxdrzywWosrblPRasvRUpZLaUbtDHGZQtvZOvHeVSTBHpitDllUljVvWrwvhpnVzeWVYhMPs
kMVcdeHzFZxTWocGvaKhhcnozRSbWsIEhpeNfJaRjLwWCvKfTLhuVsJczIYFPCyrOJxOPkXhVuCqCUgE
luwLBCmqPwDvUPuBRrJZhfEXHXSBvljqJVVfEGRUWRSHPeKUJCpMpIsrV.......
我想要的是遍历这个文本文件,找出那些被三个大写字母包围的小写字母。
为此,我写了一个Python脚本,代码如下:
import re
pattern = re.compile("[a-z][A-Z]{3}([a-z])[A-Z]{3}[a-z]")
f = open('/Users/Dev/Sometext.txt','r')
for line in f:
result = pattern.search(line)
if result:
print result.groups()
f.close()
但是这个脚本并没有返回我想要的小写字母列表,而是返回了所有符合正则表达式条件的文本块,比如:
aXCSdFGHj
vCDFeTYHa
nHJUiKJHo
.........
.........
有人能告诉我我到底哪里做错了吗?而且有没有其他方法可以在整个文件上运行正则表达式搜索,而不需要逐行遍历?
谢谢
3 个回答
import re
with open('/Users/Dev/Sometext.txt','r') as f:
tokens = re.findall(r'[a-z][A-Z]{3}([a-z])[A-Z]{3}[a-z]', f.read())
for token ins tokens:
print token
findall
的作用:
它会返回字符串中所有不重叠的模式匹配结果,结果以字符串列表的形式呈现。这个字符串是从左到右扫描的,匹配结果会按照找到的顺序返回。如果模式中有一个或多个分组,它会返回一个分组的列表;如果模式有多个分组,结果会是一个包含多个元组的列表。空匹配也会包含在结果中,除非它们与另一个匹配的开头相接触。
这可能是re
模块中最有用的函数。
read()函数会把整个文件读入一个大字符串中。如果你需要对整个文件进行正则表达式匹配,这个功能特别有用。
注意:根据文件的大小,你可能更倾向于像你第一次尝试时那样逐行读取文件。
我建议你使用“前瞻”和“后顾”的技巧:
(?<=[A-Z]{3})(?<![A-Z].{3})([a-z])(?=[A-Z]{3})(?!.{3}[A-Z])
这样做就不会出现匹配重叠的问题。
解释:
(?<=[A-Z]{3}) # assert that there are 3 uppercase letters before the current position
(?<![A-Z].{3}) # assert that there is no uppercase letter 4 characters before the current position
([a-z]) # match a lowercase character (all characters in the example are ASCII)
(?=[A-Z]{3}) # assert that there are 3 uppercase letter after the current position
(?!.{3}[A-Z]) # assert that there is no uppercase letter 4 characters after the current position
把 result.groups()
改成 result.group(1)
,这样你就能得到单个字母的匹配结果。
你代码的第二个问题是,它无法在一行中找到多个结果。所以你需要用 re.findall
或 re.finditer
,而不是 re.search
。findall
会返回字符串或者字符串的元组,而 finditer
则返回匹配对象。
这是我处理同样问题的方法:
import urllib
import re
pat = re.compile('[a-z][A-Z]{3}([a-z])[A-Z]{3}[a-z]')
print ''.join(pat.findall(urllib.urlopen(
"http://www.pythonchallenge.com/pc/def/equality.html").read()))
注意,re.findall
和 re.finditer
返回的结果是不会重叠的。所以当你用上面的模式和 re.findall
在字符串 'aBBBcDDDeFFFg'
中查找时,你只会找到 'c'
,而不会找到 'e'
。幸运的是,这个Python挑战题目没有这样的例子。