Python正则匹配单引号文本,忽略转义引号(及制表符/换行)
给定一个文本文件,其中我想匹配的字符被单引号包围,但可能有零个或一个被转义的单引号,还有零个或多个制表符和换行符(这些不需要转义)——我只想匹配文本部分。举个例子:
menu_item = 'casserole';
menu_item = 'meat
loaf';
menu_item = 'Tony\'s magic pizza';
menu_item = 'hamburger';
menu_item = 'Dave\'s famous pizza';
menu_item = 'Dave\'s lesser-known
gyro';
我想只抓取文本(和空格),忽略制表符和换行符——我其实不在乎转义的引号是否出现在结果中,只要它不影响匹配就行:
casserole
meat loaf
Tonys magic pizza
hamburger
Daves famous pizza
Dave\'s lesser-known gyro # quote is okay if necessary.
我已经创建了一个正则表达式,虽然它差不多能做到——它处理了转义的引号,但对换行符却没能处理:
menuPat = r"menu_item = \'(.*)(\\\')?(\t|\n)*(.*)\'"
for line in inFP.readlines():
m = re.search(menuPat, line)
if m is not None:
print m.group()
网上确实有很多关于正则表达式的问题——但大多数都是用Perl写的,如果有一个能满足我需求的,我也没搞明白 :) 由于我使用的是Python,所以即使它分散在多个组里,我也不在乎,重新组合起来很简单。
有些回答建议直接用代码来解析文本。虽然我相信我可以这样做——但我离有一个可用的正则表达式很近 :) 而且看起来应该是可以做到的。
更新:我刚意识到我在用Python的readlines()来获取每一行,这显然把传递给正则表达式的行分开了。我正在考虑重写这部分,但如果有任何建议也会很有帮助。
3 个回答
你可以试试这样做:
pattern = re.compile(r"menu_item = '(.*?)(?<!\\)'", re.DOTALL)
这个方法会从第一个找到的单引号开始匹配,直到遇到第一个没有被反斜杠(\)前面的单引号为止。在这两个单引号之间的换行符和制表符也会被包含在内。
这个经过测试的脚本应该能解决问题:
import re
re_sq_long = r"""
# Match single quoted string with escaped stuff.
' # Opening literal quote
( # $1: Capture string contents
[^'\\]* # Zero or more non-', non-backslash
(?: # "unroll-the-loop"!
\\. # Allow escaped anything.
[^'\\]* # Zero or more non-', non-backslash
)* # Finish {(special normal*)*} construct.
) # End $1: String contents.
' # Closing literal quote
"""
re_sq_short = r"'([^'\\]*(?:\\.[^'\\]*)*)'"
data = r'''
menu_item = 'casserole';
menu_item = 'meat
loaf';
menu_item = 'Tony\'s magic pizza';
menu_item = 'hamburger';
menu_item = 'Dave\'s famous pizza';
menu_item = 'Dave\'s lesser-known
gyro';'''
matches = re.findall(re_sq_long, data, re.DOTALL | re.VERBOSE)
menu_items = []
for match in matches:
match = re.sub('\s+', ' ', match) # Clean whitespace
match = re.sub(r'\\', '', match) # remove escapes
menu_items.append(match) # Add to menu list
print (menu_items)
这里是正则表达式的简化版本:
'([^'\\]*(?:\\.[^'\\]*)*)'
这个正则表达式使用了Jeffrey Friedl的"展开循环"效率技巧进行了优化。(详细信息请查看:《正则表达式精髓(第三版)》)
需要注意的是,上面的正则表达式和下面这个是等价的(后者更常见,但在大多数NFA正则表达式实现中速度较慢):
'((?:[^'\\]|\\.)*)'
这样做应该可以:
menu_item = '((?:[^'\\]|\\')*)'
这里的 (?:[^'\\]|\\')*
这部分是用来匹配任何字符的序列,除了 '
和 \
,或者一个字面上的 \'
。前面的表达式 [^'\\]
也允许换行符和制表符,但你需要把它们替换成一个空格。