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

2024-04-27 08:16:23 发布

您现在位置: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.

我已经创建了一个regex,几乎会这样做-它处理转义引号,但不处理换行符:

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,我不在乎它是否分布在多个组中,所以很容易重新组合它们。

有人说,一些答案只是与代码解析文本。虽然我确信我可以做到这一点-我非常接近拥有一个工作的regex:)而且似乎它应该是可行的。

更新:我刚刚意识到我正在使用Pythonreadlines()来获取每一行,这显然是在分解传递给regex的行。我正在考虑重新编写它,但任何关于这方面的建议也会非常有帮助。


Tags: 文本magicitem制表符regexmenulesserdave
3条回答

这应该做到:

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

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

这个经过测试的脚本应该可以做到:

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)

下面是regex的简短版本:

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

这个regex使用Jeffrey Friedl的“展开循环”效率技术进行优化。(参见:Mastering Regular Expressions (3rd Edition))了解详细信息。

注意,上面的regex相当于下面的regex(这在大多数NFA regex实现中更常见,但速度慢得多):

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

你可以这样试试:

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

它将在找到的第一个单引号处开始匹配,并在没有反斜杠的第一个单引号处结束。它还捕获在两个单引号之间找到的任何换行符和制表符。

相关问题 更多 >