我有一个正则表达式,例如
(ma|(t){1})
。它匹配 ma
和 t
,但不匹配 bla
。
我想否定正则表达式,因此它必须匹配
bla
而不是 ma
和 t
,通过向此正则表达式添加一些内容。我知道我可以写 bla
,但实际的正则表达式更复杂。
使用否定环视:
(?!
pattern
)
积极的环视可用于断言模式匹配。否定环视则相反:它用于断言模式不匹配。有些风格支持断言;有些则支持断言。有些对lookbehind等设置了限制。
这些是尝试提出玩具问题的正则表达式解决方案作为练习;如果您想学习使用环视的各种方法(嵌套它们、使用它们捕获等),它们应该具有教育意义:
假设您只想禁止与正则表达式完全匹配的字符串(即
mmbla
可以,但 mm
不行),这就是您想要的:
^(?!(?:m{2}|t)$).*$
(?!(?:m{2}|t)$)
是负数 lookahead;它表示“从当前位置开始,接下来的几个字符是 not mm
或 t
,后面是字符串的结尾。”开头的起始锚点 (^
) 确保在字符串的开头应用前瞻。如果成功,.*
将继续并消耗该字符串。
仅供参考,如果您使用 Java 的
matches()
方法,您实际上并不需要 ^
和最后的 $
,但它们不会造成任何伤害。不过,前瞻中的 $
是必需的。
\b(?=\w)(?!(ma|(t){1}))\b(\w*)
这是给定的正则表达式。
是找到单词边界。
正向展望 (?=\w) 是为了避免空格。
对原始正则表达式的负面展望是为了防止它的匹配。
最后 (\w*) 是捕获剩下的所有单词。
包含单词的组是第 3 组。
简单的 (?!pattern) 将不起作用,因为任何子字符串都会匹配
简单的 ^(?!(?:m{2}|t)$).*$ 将不起作用,因为它的粒度是整行
以下内容适用于 JavaScript:
^(?![\s\S]*ORIGINAL_REGEX_SOURCE)[\s\S]*$
例如,在您的情况下,您的否定正则表达式将是
^(?![\s\S]*(ma|(t){1}))[\s\S]*$
。
下面的
negate
函数可用于将正则表达式转换为其否定形式:
function negate(regex) {
return new RegExp(
String.raw`^(?![\s\S]*${regex.source})[\s\S]*$`,
regex.flags,
)
}
const tests = [
{ regex: /(ma|(t){1})/, matches: ['ma', 't'], nonMatches: ['bla'] },
{ regex: /foo/, matches: ['foo'], nonMatches: ['bar'] },
{ regex: /(foo|bar)/, matches: ['foo', 'bar'], nonMatches: ['baz'] },
{ regex: /\d{3}/, matches: ['123', '456', '999'], nonMatches: ['foo'] },
]
for (const { regex, matches, nonMatches } of tests) {
for (const text of matches) {
const atStart = `${text} ...`
const atEnd = `... ${text}`
const inMiddle = `... ${text} ...`
check(regex.test(text), `${regex} matches ${JSON.stringify(text)}`)
check(regex.test(atStart), `${regex} matches ${JSON.stringify(atStart)}`)
check(regex.test(atEnd), `${regex} matches ${JSON.stringify(atEnd)}`)
check(regex.test(inMiddle), `${regex} matches ${JSON.stringify(inMiddle)}`)
const negated = negate(regex)
check(!negated.test(text), `${negated} doesn't match ${JSON.stringify(text)}`)
check(!negated.test(atStart), `${negated} doesn't match ${JSON.stringify(atStart)}`)
check(!negated.test(atEnd), `${negated} doesn't match ${JSON.stringify(atEnd)}`)
check(!negated.test(inMiddle), `${negated} doesn't match ${JSON.stringify(inMiddle)}`)
const doubleNegated = negate(negated)
check(doubleNegated.test(text), `${doubleNegated} matches ${JSON.stringify(text)}`)
check(doubleNegated.test(atStart), `${doubleNegated} matches ${JSON.stringify(atStart)}`)
check(doubleNegated.test(atEnd), `${doubleNegated} matches ${JSON.stringify(atEnd)}`)
check(doubleNegated.test(inMiddle), `${doubleNegated} matches ${JSON.stringify(inMiddle)}`)
}
for (const text of nonMatches) {
const atStart = `${text} ...`
const atEnd = `... ${text}`
const inMiddle = `... ${text} ...`
check(!regex.test(text), `${regex} doesn't match ${JSON.stringify(text)}`)
check(!regex.test(atStart), `${regex} doesn't match ${JSON.stringify(atStart)}`)
check(!regex.test(atEnd), `${regex} doesn't match ${JSON.stringify(atEnd)}`)
check(!regex.test(inMiddle), `${regex} doesn't match ${JSON.stringify(inMiddle)}`)
const negated = negate(regex)
check(negated.test(text), `${negated} matches ${JSON.stringify(text)}`)
check(negated.test(atStart), `${negated} matches ${JSON.stringify(atStart)}`)
check(negated.test(atEnd), `${negated} matches ${JSON.stringify(atEnd)}`)
check(negated.test(inMiddle), `${negated} matches ${JSON.stringify(inMiddle)}`)
const doubleNegated = negate(negated)
check(!doubleNegated.test(text), `${doubleNegated} doesn't match ${JSON.stringify(text)}`)
check(!doubleNegated.test(atStart), `${doubleNegated} doesn't match ${JSON.stringify(atStart)}`)
check(!doubleNegated.test(atEnd), `${doubleNegated} doesn't match ${JSON.stringify(atEnd)}`)
check(!doubleNegated.test(inMiddle), `${doubleNegated} doesn't match ${JSON.stringify(inMiddle)}`)
}
}
console.info('Tests passed')
function check(condition, message = 'Condition failed') {
if (!condition) {
throw new Error(message)
}
console.info(message)
}
如果你使用 Laravel,请应用此方法。
Laravel 有一个 not_regex,其中验证的字段必须与给定的正则表达式不匹配;内部使用 PHP
preg_match
函数。
'email' => 'not_regex:/^.+$/i'