我有as和bs的字符串。我想提取所有重叠的子序列,其中子序列是单个b包围的b。这是我写的正则表达式:
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}])
代替第一个非捕获组是行不通的。我尝试改为用正向(?<=[{vowels}]|^|{diphtongues})
代替该组,但是regex不会接受不同的长度(即使删除双音不起作用,显然^
的长度也不同)。>
所以这是上面模式中的问题情况:
syllables.findall('æbə')
# ['bə']
# should be: ['æb', 'bə']
修改2: 我已改用正则表达式,它允许进行可变宽度的回溯,从而解决了该问题。令我惊讶的是,它甚至似乎比标准库中的re模块更快。不过,我仍然想知道如何使用re模块。 (:
答案 0 :(得分:1)
我建议通过两次否定来解决此问题:
(?= # 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
请注意,我将 grouping 构造替换为 lookarounds :(?:a|^)
替换为(?<![^a])
,(?:a|$)
替换为(?![^a])
。后者并不是很重要,但是第一个在这里非常重要。
外部先行模式开头的(?:a|^)
匹配a
或字符串的开头,无论哪个先出现。如果a
开头,则匹配它,当输入为abbabb
时,您会得到bbabb
,因为它与捕获组模式匹配并且紧随其后的是字符串位置的结尾。下一个迭代在第一个a
之后开始,并且找不到任何匹配项,因为字符串中仅剩下的a
在a
之后没有b
。
请注意,order of alternative matters。如果更改为(?:^|a)
,则匹配从字符串的开头开始,b*
匹配空字符串,ab*
抓取abb
中的第一个abbabb
,然后由于紧随其后的是a
,因此您将获得abb
作为匹配项。在第一个a
之后无法匹配任何内容。
答案 1 :(得分:0)
请记住,python“短路”,因此,如果它匹配“ ^”,它就不会继续寻找它是否也匹配“ a”。这将“消耗”匹配的字符,因此,在匹配“ a”的情况下,将消耗“ a”,并且下一个匹配的组将不可用“ a”,并且因为使用(?:)语法不能捕获“ “ a”是“丢失”的,不能被下一个分组(b *(?: a)b *)捕获,而当“ ^”被第一个分组占用时,第一个“ a”将与第二个分组。