正则表达式负向前查找
我想要一个正则表达式,它可以匹配所有以四个或更多数字结尾的地址,但这些数字不能出现在 'APT'
、'BOX'
、'APT '
或 'BOX '
之后。
HITME 1234
HITME 12345
HITME1234
但不应该匹配以下情况:
BOX 1234
BOX 12345
BOX4044
APT 1234
APT 12345
NONHIT123
NONHIT 123
我已经写了一个这样的表达式
(?<!(APT |BOX ))([0-9]{4,})$
但它没有正确工作。某种程度上,它还是匹配了那些不应该匹配的情况。
4 个回答
1
你可以尝试匹配下面这个正则表达式。
^(?:(?:BOX|APT) *\d{4,}|\D*\d{,3}|\D*(\d{4,}))$
如果这个字符串符合要求,捕获组1会包含字符串末尾的数字。
这个表达式有三个部分,像是选择题。前两个部分匹配那些不合法的字符串,但不把它们抓住。第三部分则匹配并抓住合法字符串中的数字。所以,重点是要关注那些能抓住子字符串的匹配结果。(这可不是我发明的奇怪方法;这是个老掉牙的技巧,自古以来就有人在用。)
我们可以把这个表达式拆解成以下几个部分。
^ # match the beginning of the string
(?: # begin a non-capture group
(?:BOX|APT) *\d{4,} # match 'BOX' or 'APT' followed by 4 or more spaces
# followed by 4 or more spaces
| # or
\D*\d{,3} # match 0 or more non-digits, followed by 0 to 3 digits
| # or
\D* # match 0 or more non-digits
( # begin capture group 1
\d{4,} # match 4 or more digits
) # end capture group 1
) # end non-capture group
$ # match end of string
注意所有可变长度的匹配( *
, \D*
, \d{4,}
和 \d{,3}
)都是 贪婪的,也就是说,它们会尽可能多地匹配字符。
1
你不能把 APT
和 BOX
放在同一个回溯断言中,因为它们的长度必须相同。
另外注意,你的模式只会匹配数字。
你可以添加另一个回溯断言,使用另外两个选项,并确保匹配的开始位置左边没有直接的数字。
在匹配数字之前,我们不能使用单词边界 \b
,因为你希望能匹配到 HITME1234
。
(?<!\bAPT |\bBOX )(?<!\bAPT|\bBOX)(?<!\d)[0-9]{4,}$
根据最后数字前允许的字符,你可以使用负向前瞻和单词边界来获取整个匹配,同时还能匹配到 BOXA 1234
。
这个模式确保从字符串的开头开始,后面没有跟着 BOX 或 APT,然后是可选的空格,最后是一个数字:
^(?!(?:BOX|APT)(?=[^\S\n]*\d))\w+[^\S\n]*\d{4,}$
0
简而言之,使用 ^(?!APT|BOX).*?([0-9]{4,})$
你的正则表达式 (?<!(APT |BOX ))([0-9]{4,})$
匹配得不太对:
BOX 12345
在2345
上匹配,因为它前面没有APT
或BOX
,而是前面有BOX 1
BOX4044
在4044
上匹配,因为它前面没有APT
或BOX
,而是前面有BOX
APT 12345
在2345
上匹配也是类似的原因。
你需要的正则表达式是 ^(?!APT|BOX).*?([0-9]{4,})$
,可以这样理解:
^(?!APT|BOX)
- 字符串的开头不能跟着APT
或BOX
.*?
- 字符串中间可以有一些杂七杂八的东西,尽量少占用字符(比如在你的测试案例中HITME
)([0-9]{4,})$
- 字符串末尾匹配的数字