【问题标题】:Python regex to match text in single quotes, ignoring escaped quotes (and tabs/newlines)Python 正则表达式匹配单引号中的文本,忽略转义引号(和制表符/换行符)
【发布时间】:2011-03-27 21:42:04
【问题描述】:

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

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() 来获取每一行,这显然会破坏传递给正则表达式的行。我正在考虑重写它,但任何关于这部分的建议也会非常有帮助。

【问题讨论】:

  • 不是重复的 - 我也在尝试处理(非转义)换行符打破我的输入数据。
  • 我同意,但我认为无论如何都值得指出。只需使用re.MULTILINE (docs.python.org/library/re.html#re.MULTILINE) 匹配多行,$ 忽略/匹配尾行,使用\s(相同链接)匹配新空格。前进,蚱蜢;)
  • 附带说明:字符串 menu_item = 'Dave\'s lesser-known \n gyro'; 不包含转义的单引号。字符串文字包含一个,但这是为了帮助 Python 将其与字符串结尾的文字引号区分开来。如果你想要带有转义单引号的实际测试用例,你需要像ohai = 'Dave\\\'s' 这样的东西。

标签: python regex


【解决方案1】:

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

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 正则表达式实现中要慢得多):

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

【讨论】:

    【解决方案2】:

    应该这样做:

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

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

    【讨论】:

    • 当您说“替换为一个空格”时,您的意思是在运行此 RE 之前 清理/删除制表符/换行符吗?当我尝试您的 RE 时,它与任何带中断的行都不匹配。
    • @John C:不,我会在之后使用re.sub(r"[\n\r\t]+", " ", match) 之类的东西。
    • 但是,我的匹配变量 m 对于有换行符的输入行是空的,所以没有什么可以替代的。
    • Arg!查看我的代码,我正在对输入文件执行 readlines() - 我认为它在换行符上中断。显然我需要重新编写更多代码(叹气)。
    • 值得注意的是,虽然此表达式适用于转义引号,但当遇到另一个转义序列(如\n 或单独的反斜杠)时,它也将起作用。您需要将您希望遇到的所有转义序列添加到组中,即:'((?:[^'\\]|\\|\\r|\\n|\\t|\\\\')*)'
    【解决方案3】:

    你冷试试这样:

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

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

    【讨论】:

    • 看起来很有趣,但正如我在另一条评论中指出的那样 - 我刚刚意识到我正在做一个 readlines(),它正在换行,所以我还有另一个问题要解决.
    猜你喜欢
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多