【问题标题】:Finding the last group in a regular expression在正则表达式中查找最后一组
【发布时间】:2010-08-06 15:35:17
【问题描述】:

三个下划线分隔的元素组成我的字符串: - 首先(字母和数字) - 中间(字母、数字和下划线) - 最后(字母和数字)

最后一个元素是可选的。

注意:我需要通过他们的名字而不是他们的索引来访问我的组。

例子:

String : abc_def
first : abc
middle : def
last : None

String : abc_def_xyz
first : abc
middle: def
last: xyz

String : abc_def_ghi_jkl_xyz
first : abc
middle : def_ghi_jkl
last : xyz

我找不到正确的正则表达式...

到目前为止,我有两个想法:

可选组

(?P<first>[a-z]+)_(?P<middle>\w+)(_(?P<last>[a-z]+))?

但是中间组匹配到字符串的结尾:

String : abc_def_ghi_jkl_xyz
first : abc
middle : def_ghi_jkl_xyz
last : vide

使用“|”

(?P<first>[a-z]+)_(?P<middle>\w+)_(?P<last>[a-z]+)|(?P<first>[a-z]+)_(?P<middle>\w+)

这个表达式无效:第一组和中间组被声明了两次。我虽然可以编写一个表达式,重用表达式第一部分的匹配组:

(?P<first>[a-z]+)_(?P<middle>\w+)_(?P<last>[a-z]+)|(?P=first)_(?P=middle)

表达式是有效的,但是像 abc_def 这样只有第一个和一个中间的字符串不匹配。

注意

这些字符串实际上是我需要匹配的路径的一部分。它可能是这样的路径:

  • /my/path/to/abc_def
  • /my/path/to/abc_def/
  • /my/path/to/abc_def/some/other/stuf
  • /my/path/to/abc_def/some/other/stuf/
  • /my/path/to/abc_def_ghi_jkl_xyz
  • /my/path/to/abc_def_ghi_jkl_xyz/
  • /my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf
  • /my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf/
  • ...

有什么想法可以仅使用正则表达式来解决我的问题吗?不能对匹配的组进行后处理。

非常感谢!

【问题讨论】:

  • /my/path/to/ 是常量还是变量?如果变量,你怎么知道路径的哪一部分包含你试图从中提取子字符串的字符串?

标签: python regex


【解决方案1】:

将中间组更改为非贪婪,并添加字符串开头和结尾的锚点:

^(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?$

默认情况下,\w+ 将尽可能匹配 much,这会吃掉字符串的其余部分。添加? 告诉它尽可能匹配little

感谢 Tim Pietzcker 指出锚点要求。

【讨论】:

  • 没有锚就行不通。在abc_def_ghi_jkl_xyz 中,first 将匹配abcmiddle 将匹配dlast 将为空。
【解决方案2】:

使用

^(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?$

^$ 将正则表达式锚定在字符串的开头和结尾。

\w+? 设为惰性可以使其匹配尽可能少(但至少匹配一个字符)。

编辑:

对于现在包含此字符串之前和之后的路径的更改要求,此方法有效:

^(.*?/)(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?(/.*)?$

代码示例(Python 3.1):

import re
paths = ["/my/path/to/abc_def",
         "/my/path/to/abc_def/",
         "/my/path/to/abc_def/some/other/stuf",
         "/my/path/to/abc_def/some/other/stuf/",
         "/my/path/to/abc_def_ghi_jkl_xyz",
         "/my/path/to/abc_def_ghi_jkl_xyz/",
         "/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf",
         "/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf/"]

regex = re.compile(r"^(.*?/)(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?(/.*)?$")

for path in paths:
    match = regex.match(path)
    print ("{}:\nBefore: {}\nFirst: {}\nMiddle: {}\nLast: {}\nAfter: {}\n".format(
           path, match.group(1), match.group("first"), match.group("middle"),
           match.group("last"), match.group(6)))

输出:

/my/path/to/abc_def:
Before: /my/path/to/
First: abc
Middle: def
Last: None
After: None

/my/path/to/abc_def/:
Before: /my/path/to/
First: abc
Middle: def
Last: None
After: /

/my/path/to/abc_def/some/other/stuf:
Before: /my/path/to/
First: abc
Middle: def
Last: None
After: /some/other/stuf

/my/path/to/abc_def/some/other/stuf/:
Before: /my/path/to/
First: abc
Middle: def
Last: None
After: /some/other/stuf/

/my/path/to/abc_def_ghi_jkl_xyz:
Before: /my/path/to/
First: abc
Middle: def_ghi_jkl
Last: xyz
After: None

/my/path/to/abc_def_ghi_jkl_xyz/:
Before: /my/path/to/
First: abc
Middle: def_ghi_jkl
Last: xyz
After: /

/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf:
Before: /my/path/to/
First: abc
Middle: def_ghi_jkl
Last: xyz
After: /some/other/stuf

/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf/:
Before: /my/path/to/
First: abc
Middle: def_ghi_jkl
Last: xyz
After: /some/other/stuf/

【讨论】:

    【解决方案3】:

    试试这个正则表达式:

    ^(?P<first>[a-z]+)_(?P<middle>[a-z]+(?:_[a-z]+)*?)(?:_(?P<last>[a-z]+))?$
    

    这是一个测试用例:

    import re
    
    strings = ['abc_def', 'abc_def_xyz', 'abc_def_ghi_jkl_xyz']
    pattern = '^(?P<first>[a-z]+)_(?P<middle>[a-z]+(?:_[a-z]+)*?)(?:_(?P<last>[a-z]+))?$'
    for string in strings:
        m = re.match(pattern, string)
        print m.groupdict()
    

    输出是:

    {'middle': 'def', 'last': None, 'first': 'abc'}
    {'middle': 'def', 'last': 'xyz', 'first': 'abc'}
    {'middle': 'def_ghi_jkl', 'last': 'xyz', 'first': 'abc'}
    

    【讨论】:

    • 我应该添加比我的字符串实际上是更大字符串的一部分,所以我不能真正使用 $ 锚。我现在正在更新主要问题。
    • @Charles:嗯,你可以用/(?:/|$)代替^$
    【解决方案4】:

    没必要那么复杂。

    >>> s="abc_def_ghi_jkl_xyz"
    >>> s.rsplit("_",1)
    >>> splitted=s.split("_")
    >>> first=splitted[0]
    >>> last=splitted[-1]
    >>> middle=splitted[1:-1]
    >>> middle='_'.join(splitted[1:-1])
    >>> print middle
    def_ghi_jkl
    

    【讨论】:

    • 我的问题是一个更大的、严重的正则表达式问题的一小部分。无论如何感谢您的回答。
    • 因为 middle 在第二条语句中被覆盖 (>>> middle=splitted[1:-1] >>> middle='_'.join( splitted[1:-1])),不需要 middle=splitted[1:-1]
    【解决方案5】:

    感谢大家的帮助!我的问题的两个关键在哪里: - 在我的模式末尾添加一个锚点 - 使中间群体不贪婪。

    所以:

    /start/of/the/path/(?P<a>[a-z]+)_(?P<b>\w+?)(_(?P<c>[a-z]+))?(/|$)
    

    这样,以下所有字符串都会匹配:

    /jobs/ads/abc_J123/previs/m_name
    /jobs/ads/abc_J123/previs/m_name/
    /jobs/ads/abc_J123/previs/m_name/some_stuff
    /jobs/ads/abc_J123/previs/m_name/some_stuff/
    /jobs/ads/abc_J123/previs/m_name/some_stuff/other_stuff
    /jobs/ads/abc_J123/previs/m_name/some_stuff/other_stuff/
    /jobs/ads/abc_J123/previs/m_name_stage
    /jobs/ads/abc_J123/previs/m_name_stage/
    /jobs/ads/abc_J123/previs/m_name_stage/some_stuff
    /jobs/ads/abc_J123/previs/m_name_stage/some_stuff/
    /jobs/ads/abc_J123/previs/m_name_stage/some_stuff/other_stuff
    /jobs/ads/abc_J123/previs/m_name_stage/some_stuff/other_stuff/
    /jobs/ads/abc_J123/previs/m_long_name_stage
    /jobs/ads/abc_J123/previs/m_long_name_stage/
    /jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff
    /jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/
    /jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/other_stuff
    /jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/other_stuff/
    

    非常感谢您的帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-24
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多