Python正则表达式格式

4 投票
2 回答
1272 浏览
提问于 2025-04-17 19:40

我正在尝试使用Python的re模块来匹配一些字符串,但一直没能正确完成。我要处理的字符串看起来像这样(示例)

XY_efgh_1234_0040_rev_2_1_NC_asdf
XY_abcd_1122Ae_1150_rev2_1_NC
XY_efgh_0124e_50_NC
asdf_1980_2234a_2
XY_abcd_5098_2270_2_1_NC
PC_bos_7659Ae_1450sp_rev_2_1_NC_GRAPH

这里的模式并不是固定的,可能会有一些变化。这对我来说很重要:

  • 忽略字符串开头的部分,直到第一个数字。这部分不重要,我不需要它,结果中应该去掉。

  • 然后总是会有四个数字,后面可以跟最多三个字母。我需要提取这一部分。

  • 接下来,在一些下划线之后(可能还有一个减号),是我需要的另一组数字,这组数字总是在两个到四个之间(...后面也可能跟着最多三个字母)

  • 在这一部分之后,可能会有更多的数字,这些数字是重要的,属于之前的值。里面也可能有字母...

  • 字符串的结尾可能包含像"NC"这样的东西,可能还有其他字符,这部分不重要,应该去掉。

所以,根据之前的示例,这就是我需要处理的内容:

('1234',   '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e',  '50')
('1980',   '2234a_2')
('5098',   '2270_2_1')
('7659Ae', '1450sp_rev_2_1')

...我从来没有在正则表达式中做过这种如果和如果不的事情,这让我很头疼。到目前为止,我得到的结果是这样的,但并不是我需要的:

pattern     = re.compile(
              r"""
              ([0-9]{4}
              [A-Z]{0,3})
              [_-]{1,3}
              ([0-9]{2,4}
              [0-9A-Z_-]{0,16})
              """,
              re.IGNORECASE | 
              re.VERBOSE
              )

if re.search(pattern, string):
    print re.findall(pattern, string)

当我在最后提到的示例上使用这个时,得到的结果是:

[(u'7659Ae', u'1450sp_rev_2_1_NC_GR')]

...差不多是我需要的 - 但我不知道怎么排除结尾的_NC_GR,而且这种简单的限制字符数量的方法也不太好。

有没有人有一个好用的解决方案来处理这个情况?

相关问题:

2 个回答

3

你需要使用一种叫做“负向前瞻”的技巧,来匹配那些后面没有跟着NC的字符。我们稍微调整一下你的正则表达式,来更清楚地展示分组:

pattern     = re.compile(r"""
              ( [0-9]{4} [A-Z]{0,3} )
              [_-]{1,3}
              ( [0-9]{2,4} (?:[0-9A-Z_-](?!NC))* )
              """, re.IGNORECASE | re.VERBOSE)

{0,16}替换成一个加粗的*量词,结果是:

>>> for match in pattern.findall(inputtext):
...     print match
... 
('1234', '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e', '50')
('1980', '2234a_2')
('5098', '2270_2_1')
('7659Ae', '1450sp_rev_2_1')

所以这个(不捕获的)分组(?:[0-9A-Z_-](?!NC))可以匹配任何数字、字母、下划线或短横线,只要它后面没有跟着NC这两个字符。

2

对我来说,Martijn的解决方案不太管用。所以我来分享我的方法。

请注意,我没有使用 re.IGNORECASE
因此,我的正则表达式能够匹配到
PC_bos_7659Ae_1450sp_rev_2_1_nc_woof
我不知道这是否正是你想要的结果。

inputtext = """XY_efgh_1234_0040_rev_2_1_NC_asdf
XY_abcd_1122Ae_1150_rev2_1_NC
XY_efgh_0124e_50_NC
asdf_1980_2234a_2
XY_abcd_5098_2270_2_1_NC
PC_bos_7659Ae_1450sp_rev_2_1_NC_GRAPH
PC_bos_7659Ae_1450sp_rev_2_1_nc_woof"""
print inputtext

.

import re

print """\n----------------------------------------
WANTED
('1234',   '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e',  '50')
('1980',   '2234a_2')
('5098',   '2270_2_1')
('7659Ae', '1450sp_rev_2_1')"""
print '----------- eyquem ----------------------'
ri = re.compile('^\D+'
                '(\d{4}[a-zA-Z]{0,3})'
                '[_-]+'
                '(.+?)'
                '(?:[_-]+NC.*)?$',
                re.MULTILINE)

for match in ri.findall(inputtext):
    print match
    
print '----------- Martijn ----------------------'
ro     = re.compile(
              r"""
              ([0-9]{4}
              [A-Z]{0,3})
              [_-]{1,3}
              ([0-9]{2,4}
              [0-9A-Z_-]{0,16}?)
              (?:[-_]NC)?
              """,
              re.IGNORECASE | re.VERBOSE)

for match in ro.findall(inputtext):
    print match

结果

----------------------------------------
WANTED
('1234',   '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e',  '50')
('1980',   '2234a_2')
('5098',   '2270_2_1')
('7659Ae', '1450sp_rev_2_1')
----------- eyquem ----------------------
('1234', '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e', '50')
('1980', '2234a_2')
('5098', '2270_2_1')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1_nc_woof')
----------- Martijn ----------------------
('1234', '0040')
('1122Ae', '1150')
('0124e', '50')
('1980', '2234')
('5098', '2270')
('7659Ae', '1450')
('7659Ae', '1450')

我的正则表达式可以在单独的行上使用:

for s in inputtext.splitlines(True):
    print ri.match(s).groups()

结果一样

.

编辑

import re

inputtext = """XY_efgh_1234_0040_rev_2_1_NC_asdf
XY_abcd_1122Ae_1150_rev2_1_NC
XY_efgh_0124e_50_NC
XY_efgh_0228e_66-__NC
asdf_1980_2234a_2   
asdf_2999_133a
XY_abcd_5098_2270_2_1_NC
XY_abcd_6099_33370_2_1_NC
XY_abcd_6099_3370abcd_2_1_NC
PC_bos_7659Ae_1450sp_rev_2_1_NC_GRAPH
PC_bos_7659Ae_1450sp_rev_2_1___NC_GRAPH
PC_bos_7659Ae_1450sp_rev_2_1_nc_woof_NC
PC_bos_7659Ae_1450sp_rev_2_1_anc_woof_NC
PC_bos_7659Ae_1450sp_rev_2_1_abNC_woof_NC"""

print '----------- Martijn 2 ------------'
ruu     = re.compile(r"""
              ( [0-9]{4} [A-Z]{0,3} )
              [_-]{1,3}
              ( [0-9]{2,4} (?:[0-9A-Z_-](?!NC))* )
              """, re.IGNORECASE | re.VERBOSE)
for match in ruu.findall(inputtext):
    print match
print '----------- eyquem 2 ------------'
rii = re.compile('[_-]'
                '(\d{4}[A-Z]{0,3})'
                '[_-]{1,3}'
                '('
                  '(?=\d{2,4}[A-Z]{0,3}(?![\dA-Z]))'
                  '(?:[0-9A-Z_-]+?)'
                 ')'
                '(?:[-_]+NC.*)?'
                '(?![0-9A-Z_-])',
                re.IGNORECASE)
for m in rii.findall(inputtext):
    print m

结果

----------- Martijn 2 ------------
('1234', '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e', '50')
('0228e', '66-_')
('1980', '2234a_2')
('2999', '133a')
('5098', '2270_2_1')
('6099', '33370_2_1')
('6099', '3370abcd_2_1')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1__')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1_')
('7659Ae', '1450sp_rev_2_1_a')
----------- eyquem 2 ------------
('1234', '0040_rev_2_1')
('1122Ae', '1150_rev2_1')
('0124e', '50')
('0228e', '66')
('1980', '2234a_2')
('2999', '133a')
('5098', '2270_2_1')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1')
('7659Ae', '1450sp_rev_2_1_anc_woof')
('7659Ae', '1450sp_rev_2_1_abNC_woof')

备注:

  • 我的正则表达式无法匹配到 '33370_2_1' 或 '3370abcd_2_1',因为它们不符合“2到4个字母后面可能跟着最多3个数字”的模式。
    而Martijn的解决方案可以匹配到这些。

  • 我这个正则表达式匹配到的部分结尾是干净的;而Martijn的代码则不是。

  • Martijn的正则表达式在每个NC或nc的序列前都会停止,即使这些序列前面没有下划线,也就是说即使这些序列是想要部分的一部分字母。
    如果你不想要我这个正则表达式的这种特性,请告诉我,我会进行修改。

撰写回答