Python正则表达式匹配单引号中的文本,忽略转义引号(和制表符/换行符)

时间:2011-03-27 21:42:05

标签: python regex

给定一个文本文件,我要匹配的字符由单引号分隔,但可能有零个或一个转义的单引号,以及零个或多个制表符和换行符(未转义) - 我想要仅匹配文本。例如:

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 个答案:

答案 0 :(得分:12)

这个经过测试的脚本应该可以解决问题:

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的“展开循环”效率技术优化此正则表达式。 (有关详细信息,请参阅:Mastering Regular Expressions (3rd Edition))。

请注意,上面的正则表达式相当于下面的正则表达式(在大多数NFA正则表达式实现中更常见,但速度要慢得多):

'((?:[^'\\]|\\.)*)'

答案 1 :(得分:3)

这应该这样做:

menu_item = '((?:[^'\\]|\\')*)'

此处(?:[^'\\]|\\')*部分匹配除'\或文字\'之外的任何字符的任何序列。前表达式[^'\\]也允许换行符和制表符,然后您需要用单个空格替换。

答案 2 :(得分:2)

你冷试试这样:

pattern = re.compile(r"menu_item = '(.*?)(?<!\\)'", re.DOTALL)

它将在它找到的第一个单引号处开始匹配,并在第一个单引号结束,而不是以反斜杠开头。它还捕获两个单引号之间的任何换行符和标签。