【问题标题】:Regular expression to match anything between combination of quotes正则表达式匹配引号组合之间的任何内容
【发布时间】:2016-11-03 00:39:08
【问题描述】:

[从我的旧 question 跟进,提供更好的描述和链接]

尝试匹配两个符号之间的任何字符(包括换行符、制表符、空格等),包括那些符号。

例如:

foobar89\n\nfoo\tbar; '''废话废话'8&^"'''

需要匹配

''废话废话'8&^"'''

fjfdaslfdj; '''废话\n废话\n\t\t废话\n'8&^"'''

需要匹配

'''废话\n废话\n\t\t废话\n'8&^"'''

我正在测试正则表达式的 Python 代码(取自并改编自 here):

import collections
import re

Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column'])

def tokenize(code):
    token_specification = [
        ('BOTH',      r'([\'"]{3}).*?\2'), # for both triple-single quotes and triple-double quotes
        ('SINGLE',    r"('''.*?''')"),     # triple-single quotes 
        ('DOUBLE',    r'(""".*?""")'),     # triple-double quotes 
        # regexes which match OK
        ('COM',       r'#.*'),
        ('NEWLINE', r'\n'),           # Line endings
        ('SKIP',    r'[ \t]+'),       # Skip over spaces and tabs
        ('MISMATCH',r'.'),            # Any other character
    ]

    test_regexes = ['COM', 'BOTH', 'SINGLE', 'DOUBLE']

    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
    line_num = 1
    line_start = 0
    for mo in re.finditer(tok_regex, code):
        kind = mo.lastgroup
        value = mo.group(kind)
        if kind == 'NEWLINE':
            line_start = mo.end()
            line_num += 1
        elif kind == 'SKIP':
            pass
        elif kind == 'MISMATCH':
            pass
        else:
            if kind in test_regexes:
                print(kind, value)
            column = mo.start() - line_start
            yield Token(kind, value, line_num, column)

f = r'C:\path_to_python_file_with_examples_to_match'

with open(f) as sfile:
    content = sfile.read()

for t in tokenize(content):
    pass #print(t)

file_with_examples_to_match 在哪里:

import csv, urllib

class Q():
    """
    This class holds lhghdhdf hgh dhghd hdfh ghd fh.
    """

    def __init__(self, l, lo, d, m):
        self.l= l
        self.lo= longitude
        self.depth = d
        self.m= m

    def __str__(self):
        # sdasda fad fhs ghf dfh
        d= self.d
        if d== -1:
            d= 'unknown'
        m= self.m
        if m== -1:
            d= 'unknown'

        return (m, d, self.l, self.lo)

foobar89foobar; '''blah qsdkfjqsv,;sv
                   vqùlvnqùv 
                   dqvnq
                   vq
                   v

blah blah'8&^"'''
fjfdaslfdj; '''blah blah
     blah
    '8&^"'''

this answer 开始,我尝试r"('''.*?''')|"r'(""".*?""") 匹配三个单引号和三个双引号的情况,但均未成功。尝试r'([\'"]{3}).*?\2') 时相同。

我已经设置了一个 online 正则表达式测试器,其中一些正则表达式确实匹配,但在上面的代码中它们失败了。

我有兴趣了解 Python 的正则表达式,因此我希望能得到一个解决方案(也许是一个有效的正则表达式来对我的代码进行所需的匹配)和一个简短的解释,以便我能看到我的缺点。

【问题讨论】:

  • 我想我无法理解您在寻找什么。由于 python 正则表达式的贪婪性质,'.*' 应该捕获两个撇号之间的任何内容,包括任何撇号。究竟是什么问题?
  • @JasonBray 问题是我试图匹配 3 个连续双引号或 3 个连续单引号之间的任何内容。当我使用正则表达式 r"('''.*?''')"r'(""".*?""")'r'([\'"]{3}).*?\2') 时,即使在线正则表达式测试人员显示这些正则表达式确实匹配,但当它们在我的描述中的代码中使用时,它们不匹配。寻找理解原因。

标签: python regex python-3.x quotes


【解决方案1】:

您可能缺少使 . 也匹配换行符的标志

re.finditer(tok_regex, code, flags = re.DOTALL)

在这种情况下,输出是

('BOTH', '"""\n    This class holds lhghdhdf hgh dhghd hdfh ghd fh.\n    """')
('COM', '# sdasda fad fhs ghf dfh\n        d= self.d\n        if d== -1:\n            d= \'unknown\'\n        m= self.m\n        if m== -1:\n            d= \'unknown\'\n\n        return (m, d, self.l, self.lo)\n\nfoobar89foobar; \'\'\'blah qsdkfjqsv,;sv\n                   vq\xc3\xb9lvnq\xc3\xb9v \n                   dqvnq\n                   vq\n                   v\n\nblah blah\'8&^"\'\'\'\nfjfdaslfdj; \'\'\'blah blah\n     blah\n    \'8&^"\'\'\'')

COM 现在匹配太多了,因为. 现在将所有内容都保存到文件末尾。如果我们稍微修改一下这个模式,让它不那么贪婪

('COM',       r'#.*?$')

我们现在可以使用re.MULTILINE 使其匹配更少

re.finditer(tok_regex, code, flags = re.DOTALL | re.MULTILINE)

现在的输出是

('BOTH', '"""\n    This class holds lhghdhdf hgh dhghd hdfh ghd fh.\n    """')
('COM', '# sdasda fad fhs ghf dfh')
('BOTH', '\'\'\'blah qsdkfjqsv,;sv\n                   vq\xc3\xb9lvnq\xc3\xb9v \n                   dqvnq\n                   vq\n                   v\n\nblah blah\'8&^"\'\'\'')
('BOTH', '\'\'\'blah blah\n     blah\n    \'8&^"\'\'\'')

如果你绝对不想使用标志,你可以使用一种“hack”来避免.,因为这个元字符几乎匹配所有内容,除了换行符。您可以创建一个匹配组,它将匹配除一个符号之外的所有内容,该符号极不可能出现在您要解析的文件中。例如,您可以使用 ASCII 代码为 0 的字符。此类字符的正则表达式为 \x00,对应的模式 [^\x00] 将匹配每个符号(甚至是换行符),但 ASCII 代码为 0 的符号除外(这就是为什么它是hack,您无法匹配没有标志的每个符号)。您需要为COM 保留初始正则表达式,而对于BOTH,它将是

('BOTH',      r'([\'"]{3})[^\x00]*?\2')

强烈推荐使用正则表达式的在线工具来解释它们,例如regex101

对于更复杂的引用匹配情况,您需要编写一个解析器。例如,参见 Can the csv format be defined by a regex?When you should NOT use Regular Expressions?

【讨论】:

  • 我明白为什么正则表达式不适合这类问题,而解析器确实更合适(顺便说一句,链接很好)。但是,这对我来说更像是一种方法/练习,可以准确地了解正则表达式的局限性。对于这种情况,我的目标是一个足够好的正则表达式,它可以匹配 3 个单引号或双引号之间的任何内容。
  • 那是完美的。我从来没有想过flags 是解决方案。我想知道除了修改COM 使其不那么贪婪之外,其他正则表达式是否也需要类似的修改。例如如果我也有('NUMBER', r'\d+(\.\d*)?')('ID', r'[A-Za-z]+') 他们是否也需要修改以减少贪婪?
  • @nk-fford [^*]. 的不同之处在于第一组匹配除* 之外的所有内容(包括换行符),而. 匹配几乎所有符号,但不匹配换行符默认。从理论上讲,您可以将. 替换为使用单个符号的负组,该符号永远不会出现在文本中,但是,它会使您的正则表达式不那么可读
  • @nk-fford 好吧,这更像是一个 hack,而不是一个正确的解决方案,但假设我们在 BOTH 的文本正则表达式中永远不会得到 \x00 将是 r'([\'"]{3})[^\x00]*?\2' 和 @987654350 @r'#.*'。这样它就可以在没有设置任何标志的情况下工作,并在答案中提供最后一个输出
  • @nk-fford 在答案中包含了 hack 描述,并详细解释了 [^\x00] 是什么
猜你喜欢
  • 2018-04-04
  • 2014-02-04
  • 1970-01-01
  • 2016-10-22
  • 2011-09-06
  • 2017-05-25
  • 1970-01-01
  • 1970-01-01
  • 2020-01-20
相关资源
最近更新 更多