在正则表达式中查找最后一个组

时间:2010-08-06 15:35:18

标签: python regex

三个下划线分隔元素构成我的字符串: - 第一个(字母和数字) - 中间(字母,数字和下划线) - 最后(字母和数字)

最后一个元素是可选的。

注意:我需要按名字访问我的群组,而不是他们的索引。

示例:

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不匹配。

注意

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

  • /我的/路径/到/ abc_def
  • /我的/路径/到/ abc_def /
  • /我的/路径/到/ abc_def /一些/其他/ STUF
  • /我的/路径/到/ abc_def /一些/其他/ STUF /
  • /我的/路径/到/ abc_def_ghi_jkl_xyz
  • /我的/路径/到/ abc_def_ghi_jkl_xyz /
  • /我的/路径/到/ abc_def_ghi_jkl_xyz /一些/其他/ STUF
  • /我的/路径/到/ abc_def_ghi_jkl_xyz /一些/其他/ STUF /
  • ...

任何想法只用正则表达式解决我的问题?对匹配的组进行后处理不是一种选择。

非常感谢!

5 个答案:

答案 0 :(得分:4)

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

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

默认情况下,\w+将匹配为,这会占用其余字符串。添加?会使其匹配为 little

感谢Tim Pietzcker指出锚定要求。

答案 1 :(得分:1)

使用

^(?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/

答案 2 :(得分:0)

试试这个正则表达式:

^(?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'}

答案 3 :(得分:0)

不需要那么复杂。

>>> 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

答案 4 :(得分:0)

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

所以:

/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/

非常感谢你的帮助!