在字符串开头,正则表达式没有匹配

2024-07-01 07:56:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一连串的as和bs。我想提取所有重叠的子序列,其中一个子序列是由任意数量的b包围的单个a。这是我写的正则表达式:

import re

pattern = """(?=            # inside lookahead for overlapping results
             (?:a|^)        # match at beginning of str or after a
             (b* (?:a) b*)  # one a between any number of bs
             (?:a|$))       # at end of str or before next a
          """
a_between_bs = re.compile(pattern, re.VERBOSE)

它似乎按预期工作,除非字符串中的第一个字符是a,在这种情况下,会丢失以下子序列:

a_between_bs.findall("bbabbba")
# ['bbabbb', 'bbba']
a_between_bs.findall("abbabb")
# ['bbabb']

我不明白发生了什么事。如果我改变潜在匹配的开始顺序,结果也会改变:

pattern = """(?=
             (?:^|a)        # a and ^ swapped
             (b* (?:a) b*)
             (?:a|$))
          """
a_between_bs = re.compile(pattern, re.VERBOSE)

a_between_bs.findall("abbabb")
# ['abb']

我本以为这是对称的,所以以a结尾的字符串也可能会丢失,但事实似乎并非如此。怎么回事?你知道吗

编辑:

我假设上述玩具示例的解决方案将转化为我的全部问题,但事实似乎并非如此,因此我现在正在详细说明(对此表示抱歉)。我试图从转录的单词中提取“音节”。“音节”是元音或双元音,前面和后面有任意数量的辅音。这是我提取它们的正则表达式:

vowels = 'æɑəɛiɪɔuʊʌ'
diphtongues = "|".join(('aj', 'aw', 'ej', 'oj', 'ow'))
consonants = 'θwlmvhpɡŋszbkʃɹdnʒjtðf'

pattern = f"""(?=
          (?:[{vowels}]|^|{diphtongues})
          ([{consonants}]* (?:[{vowels}]|{diphtongues}) [{consonants}]*)
          (?:[{vowels}]|$|{diphtongues})
          )
          """
syllables = re.compile(pattern, re.VERBOSE)

有点棘手的是,双音节以辅音(j或w)结尾,我不想在下一个音节中包含这些辅音。因此,用双负(?<![{consonants}])替换第一个非捕获组是行不通的。我试着用一个正向的lookahead (?<=[{vowels}]|^|{diphtongues})来替换这个组,但是regex不会接受不同的长度(即使删除diphlugues也不起作用,显然^的长度不同)。你知道吗

这就是上述模式的问题所在:

syllables.findall('æbə')
# ['bə'] 
# should be: ['æb', 'bə']

编辑2: 我已经改用regex,它允许可变宽度lookbehinds,这就解决了这个问题。令我惊讶的是,它甚至比标准库中的re模块还要快。不过,我还是想知道如何让这个模块和re模块一起工作。(:


Tags: 模块ofreverbosebs序列betweenpattern
2条回答

我建议用双重否定来解决这个问题:

(?=         # inside lookahead for overlapping results
 (?<![^a])  # match at beginning of str or after a
 (b*ab*)    # one a between any number of bs
 (?![^a])   # at end of str or before next a
)

参见regex demo

注意我用lookarounds替换了分组结构:(?:a|^)替换为(?<![^a])(?:a|$)替换为(?![^a])。后者并不重要,但第一个在这里非常重要。你知道吗

外部lookahead模式开头的(?:a|^)与字符串的a或开头匹配,无论哪个先到。如果a在开始处,那么它是匹配的,当输入是abbabb时,您会得到bbabb,因为它匹配捕获组模式,并且后面有一个字符串结束位置。下一次迭代在第一个a之后开始,并且找不到任何匹配项,因为字符串中只剩下的ab之后没有a

注意order of alternative matters。如果更改为(?:^|a),则匹配从字符串的开头开始,b*匹配空字符串,ab*获取abbabb中的第一个abb,并且由于紧随其后的是a,因此将abb作为匹配。无法匹配第一个a之后的任何内容。你知道吗

记住python“短路”,所以,如果它匹配“^”,就不会继续查看它是否也匹配“a”。这将“消耗”匹配字符,因此在匹配“a”的情况下,“a”将被消耗并且不可用于下一个要匹配的组,并且因为使用(?)?:)语法是非捕获的,即“a”是“丢失的”,并且不能被下一个分组捕获(b*(?:a)b*),而当第一个分组使用“^”时,第一个“a”将在第二个分组中匹配。你知道吗

相关问题 更多 >

    热门问题