Python正则表达式匹配2个不同的分隔符

时间:2013-07-04 15:18:30

标签: python regex regex-negation

我正在尝试制作一个与之类似的正则表达式:

[[uid::page name|page alias]]

例如:

[[nw::Home|Home page]]

uid和页面别名都是可选的。

我想允许分隔符::|仅出现一次,并且仅按照显示的顺序出现。但是,在uid之后的任何地方都应该允许使用字符:。这就是问题所在。

以下正则表达式非常有效,除了它匹配::出现两次或错误位置的字符串:

regex = r'\[\[([\w]+::)?([^|\t\n\r\f\v]+)(\|[^|\t\n\r\f\v]+)?\]\]'
re.match(regex, '[[Home]]') # matches, good
re.match(regex, '[[Home|Home page]]') # matches, good
re.match(regex, '[[nw::Home]]') # matches, good
re.match(regex, '[[nw::Home|Home page]]') # matches, good
re.match(regex, '[[nw|Home|Home page]]') # doesn't match, good
re.match(regex, '[[nw|Home::Home page]]') # matches, bad
re.match(regex, '[[nw::Home::Home page]]') # matches, bad

我已经阅读了所有关于负向前瞻和后瞻性表达的内容但我无法弄清楚如何在这种情况下应用它们。任何建议将不胜感激。

修改:我还想知道如何阻止分隔符包含在匹配结果中,如下所示:

('nw::', 'Home', '|Home page')

3 个答案:

答案 0 :(得分:4)

如果我理解您的需求,您可以使用:

\[\[(?:(?<uid>\w+)::)?(?!.*::)(?<page>[^|\t\n\r\f\v]+)(?:\|(?<alias>[^|\t\n\r\f\v]+))?\]\]
                      ^^^^^^^^

有关演示,请参阅here。在uid捕获后,我添加了一个负向前瞻。

我已经为捕获的组提供了名称,但如果您不想要它们,那就是没有命名捕获组的那个:

\[\[(?:(\w+)::)?(?!.*::)([^|\t\n\r\f\v]+)(?:\|([^|\t\n\r\f\v]+))?\]\]

答案 1 :(得分:0)

那么,你怎么看待这个:

import re

regex = r'''
    \[\[                            # opening [[
        ([\w ]+)                    # first word (with possible spaces)
        (?:
            ::                      # the two colons
            (                       # second word (with possible spaces and single colons)
                [\w ]+              # word characters and spaces
                (?:
                    :               # a colon
                    [\w ]+          # word characters and spaces
                )*                  # not required, but can repeat unlimitted
            )
        )?                          # not required
        (?:
            \|                      # a pipe
            ([\w ]+)                # thid word (with possible spaces)
        )?
    \]\]                            # closing ]]
'''

test_strings = (
    '[[Home]]',
    '[[Home|Home page]]',
    '[[nw::Home]]',
    '[[nw::Home|Home page]]',
    '[[nw|Home|Home page]]',
    '[[nw|Home::Home page]]',
    '[[nw::Home::Home page]]',
    '[[nw::Home:Home page]]',
    '[[nw::Home:Home page|Home page]]'
)

for test_string in test_strings:
    print re.findall(regex, test_string, re.X)

输出:

[('Home', '', '')]
[('Home', '', 'Home page')]
[('nw', 'Home', '')]
[('nw', 'Home', 'Home page')]
[]
[]
[]
[('nw', 'Home:Home page', '')]

它不使用lookaheads / behinds。它确实允许在第一个::之后的字符串中的单冒号(如最后两个测试字符串所示)。正则表达式的简短版本是:

\[\[([\w ]+)(?:::([\w ]+(?::[\w ]+)*))?(?:\|([\w ]+))?\]\]

唯一的问题是你必须检查第二个匹配是否为空,如果是,则没有双冒号(::)并且你应该使用第一个匹配,通常字符串前面的字符串将是

答案 2 :(得分:0)

这有用吗? - http://ideone.com/NeIouP

import re
regex = r'\[\[(([\w]+)::)?([^|\t\n\r\f\v]+)(\|([^\t\n\r\f\v]+))?\]\]'
print re.match(regex, '[[Home]]').group(2,3,5) # matches, good
print re.match(regex, '[[Home|Home page]]').group(2,3,5) # matches, good
print re.match(regex, '[[nw::Home]]').group(2,3,5) # matches, good
print re.match(regex, '[[nw::Home|Home page]]').group(2,3,5) # matches, good
print re.match(regex, '[[nw|Home|Home page]]').group(2,3,5) # doesn't match, good
print re.match(regex, '[[nw|Home::Home page]]').group(2,3,5) # matches, bad
print re.match(regex, '[[nw::Home::Home page]]').group(2,3,5) # matches, bad