我目前有以下正则表达式

ZL[^0-9].{16}_.{3}PAD_N.{26}\.PIC

匹配文件名如下

ZLF_1177_0771428479_534PAD_N0530130SALP09217_1100LMV01.PIC

但想改变正则表达式,使位置上的 9 个字符SALP09217
不能占用范围

SALP00000-00899, SALP01000-03099 and SALP05000-06999

(请注意SALT00000-00899, 或除 之外的任何其他子字符串SALP都是允许的,只有以 开头的子字符串SALP才会被排除)

以下正则表达式部分有效

ZL[^0-9].{16}_.{3}PAD_N.{7}(?!(SALP00[0-8][0-9][0-9])|(SALP0[1-3]0[0-9][0-9])|(SALP0[5-6][0-9][0-9][0-9])).*\.PIC

但允许的字符串比原始正则表达式允许的字符串更大。例如,它允许

ZLF_1177_0771428479_534PAD_N0530130SALP09217_1100LMV01.PIC

这是正确的,但也是

ZLF_1177_0771428479_534PAD_N0530130SALP09217_1100LMV01LARGER.PIC

这不是

“理想”的正则表达式是

ZL[^0-9].{16}_.{3}PAD_N.{7}(?!(SALP00[0-8][0-9][0-9])|(SALP0[1-3]0[0-9][0-9])|(SALP0[5-6][0-9][0-9][0-9])).{10}\.PIC

ZLF_1177_0771428479_534PAD_N0530130SALP09217_1100LMV01.PIC

不会匹配。

有什么建议么?

14

  • 1
    我建议使用白名单方法重写要求。


    – 

  • 2
    “是的,我必须使用正则表达式。”只是出于好奇,为什么?


    – 

  • 2
    Regex 对于范围来说是一个糟糕的工具 – 即使是 1 位数字的范围。但是对于要排除的 3 个范围(或要包含的 4 个范围)- 每个 5 位数字 – 它相当疯狂。可行,但很糟糕。正如这里的其他朋友所建议的:请根据任务的细节找到不同的方法。


    – 

  • 1
    而不是.*你需要,.{19}因为前瞻不消耗任何东西。你需要将 1000-3099 范围分成 1000-2999、3000-3099


    – 

  • 2
    ZL[^0-9].{16}_.{3}PAD_N.{7}(?!SALP0(0[0-8]|[1256][0-9]|30)[0-9]{2}).{19}\.PIC


    – 



最佳答案
3

您可以使用

ZL[^0-9].{16}_.{3}PAD_N.{7}(?!SALP0(?:0(?:0{0,2}[0-9]|0?[1-9][0-9]|[1-8][0-9]{2})|(?:100[0-9]|10[1-9][0-9]|1[1-9][0-9]{2}|2[0-9]{3}|30[0-9]{2})|(?:500[0-9]|50[1-9][0-9]|5[1-9][0-9]{2}|6[0-9]{3}))(?!\d)).{19}\.PIC

参见

(?!SALP0(?:0(?:0{0,2}[0-9]|0?[1-9][0-9]|[1-8][0-9]{2})|(?:100[0-9]|10[1-9][0-9]|1[1-9][0-9]{2}|2[0-9]{3}|30[0-9]{2})|(?:500[0-9]|50[1-9][0-9]|5[1-9][0-9]{2}|6[0-9]{3})))如果当前位置右侧紧邻 SALP00000-00899、SALP01000-03099 和 SALP05000-06999 范围,则负向前瞻匹配失败

笔记:

  • SALP00(?:0{0,2}[0-9]|0?[1-9][0-9]|[1-8][0-9]{2})匹配 SALP00000-00899,
  • SALP0(?:100[0-9]|10[1-9][0-9]|1[1-9][0-9]{2}|2[0-9]{3}|30[0-9]{2})匹配 SALP01000-03099 和
  • SALP0(?:500[0-9]|50[1-9][0-9]|5[1-9][0-9]{2}|6[0-9]{3})与 SALP05000-06999 匹配。
  • (?!\d)在前瞻中的替代方案之后,确保数字被完整检查,而不是部分检查,以便SALP00903可以匹配诸如此类的值
  • 另外,看看我是如何将.{26}模式划分为.{7}并使.{19}前瞻工作起来的。

1

  • @CarySwoveland 这是由于前瞻中的替代方案部分匹配造成的,因此这是一个简单的修复。


    – 

鉴于 Wiktor 的专业知识,我无法判断他的回答是否是认真的。所需的负面前瞻可以写得更简单。

给定排除范围:

SALP00000-00899
SALP01000-03099
SALP05000-06999

SALP0一切皆有开始和结束,这是显而易见的[0-9]{2}

则剩余两位数字为:

0[0-8]
1[0-9] 2[0-9] 30
5[0-9] 6[0-9]

可以重新分组:

0[0-8]
1[0-9] 2[0-9] 5[0-9] 6[0-9]
30

并合并为:0[0-8]|[1256][0-9]|30

因此,整个负面前瞻只是:

(?!SALP0(0[0-8]|[1256][0-9]|30)[0-9]{2})

如上所述,它通过在适当的偏移处一分为二来合并.{26}。请注意,环视不消耗任何字符,因此总长度不会改变:

ZL[^0-9].{16}_.{3}PAD_N.{7}(?!SALP0(?:0[0-8]|[1256][0-9]|30)[0-9]{2}).{19}\.PIC

当且仅当字符串与以下正则表达式匹配时,它才有效。

(?x)             # invoke extended (aka free-spacing) mode
^                # match beginning of string
ZL\D             # match 'ZL' then a char other than a digit
.{16}_.{3}       # match 16 characters other than line terminators
                 # (COTLTs), then literal then 3 CORTLs
PAD_N            # match literal
\d{7}            # match 7 digits
(?:              # begin non-capture group
  (?!SALP)       # negative lookahead asserts that next 4 chars are not 'SALP'
  .{9}           # match 9 COTLTs
|                # or
  SALP           # match literal
  (?:            # begin a non-capture group
    009\d{2}     # match a literal then 2 digits
  |              # or
    03[1-9]\d{2} # match a literal then a char in the char case then 2 digits
  |              # or
    0[4789]\d{3} # match a literal then char in char class then 3 digits
  |              # or
    [1-9]\d{4}   # match char in char class then 4 digits
  )              # end non-capture class
)                # end non-capture class
_.{9}            # match literal then 9 COTLTs
\.PIC$           # match literal at end of string

5

  • 2
    这很可能是真正想要的,但从技术上讲,提供的规范允许SALP在这 9 个位置上使用非数字和非数字,但这个正则表达式不允许


    – 


  • user2175783,我修改了我的答案以符合您对问题的澄清。如果我对问题的理解仍然不正确,请告诉我。@jhnc,您说得对。


    – 


  • 我不认为这种双重否定比直接说明什么是不允许的(你现在正在部分地这样做)更清楚或更不容易出错。你似乎遗漏了SALP03[1-9][0-9]{2}


    – 


  • @jhnc,谢谢你指出我的疏忽。根据我对这个问题的新理解,我对 后面五位数字定义的“范围”可以采取的两种方法并不感兴趣'SALP',但我认为在答案中(以及在你的评论中)同时体现这两种方法都是值得的。


    – 

  • 1
    我只是发现长代码令人困惑:-) 顺便说一句,你仍然缺少 032、033、034 等。


    –