【问题标题】:Python regex to match 2 distinct delimitersPython正则表达式匹配2个不同的分隔符
【发布时间】:2013-07-04 15:18:29
【问题描述】:

我正在尝试制作一个匹配如下内容的正则表达式:

[[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')

【问题讨论】:

  • 您能否详细说明“但是,字符 : 应该允许在 uid 之后的任何位置”是什么意思?您给出的所有匹配项/不匹配项似乎都没有任何奇怪的字符顺序外观。
  • 这有点类似于为C cmets编写正确的正则表达式的问题:/* ** */可以做到,但是比较棘手。查找“C 注释正则表达式”以获取想法。

标签: python regex regex-negation


【解决方案1】:

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

\[\[(?:(?<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]+))?\]\]

【讨论】:

  • ?P&lt;..&gt; 而不是 ?&lt;..&gt;
  • @falsetru:谢谢!我忘记了 python 命名捕获组的方式略有不同。应该是这样的:\[\[(?:(?P&lt;uid&gt;\w+)::)?(?!.*::)(?P&lt;page&gt;[^|\t\n\r\f\v]+)(?:\|(?P&lt;alias&gt;[^|\t\n\r\f\v]+))?\]\]
  • @nw。惊人的!我怕我错过了什么!
【解决方案2】:

那么,你怎么看这个:

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', '')]

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

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

唯一的事情是你必须检查第二个匹配是否为空,如果是,则没有双冒号(::),你应该使用第一个匹配,通常在冒号之前的字符串。

【讨论】:

    【解决方案3】:

    这行得通吗? - 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多