Reputation: 64737
I have a regex, for example (ma|(t){1})
. It matches ma
and t
and doesn't match bla
.
I want to negate the regex, thus it must match bla
and not ma
and t
, by adding something to this regex. I know I can write bla
, the actual regex is however more complex.
Upvotes: 147
Views: 272676
Reputation: 5926
The following works in JavaScript:
^(?![\s\S]*(?:ORIGINAL_REGEX_SOURCE))[\s\S]*$
So for example, in your case, your negated regex would be ^(?![\s\S]*(ma|(t){1}))[\s\S]*$
.
The negate
function below can be used to convert a regex into its negated form:
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'] },
]
const results = []
function check(condition, message = 'Condition failed') {
if (!condition) {
throw new Error(message)
}
results.push(message)
}
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(['', ...results].join('\n'))
console.info('All tests passed')
Upvotes: 0
Reputation: 37
This regexp math your condition:
^.*(?<!ma|t)$
Look at how it works: https://regex101.com/r/Ryg2FX/1
Upvotes: -1
Reputation: 2154
Apply this if you use laravel.
Laravel has a not_regex where field under validation must not match the given regular expression; uses the PHP preg_match
function internally.
'email' => 'not_regex:/^.+$/i'
Upvotes: -2
Reputation: 743
\b(?=\w)(?!(ma|(t){1}))\b(\w*)
this is for the given regex.
the \b is to find word boundary.
the positive look ahead (?=\w) is here to avoid spaces.
the negative look ahead over the original regex is to prevent matches of it.
and finally the (\w*) is to catch all the words that are left.
the group that will hold the words is group 3.
the simple (?!pattern) will not work as any sub-string will match
the simple ^(?!(?:m{2}|t)$).*$ will not work as it's granularity is full lines
Upvotes: 2
Reputation: 383746
Use negative lookaround: (?!
pattern
)
Positive lookarounds can be used to assert that a pattern matches. Negative lookarounds is the opposite: it's used to assert that a pattern DOES NOT match. Some flavor supports assertions; some puts limitations on lookbehind, etc.
These are attempts to come up with regex solutions to toy problems as exercises; they should be educational if you're trying to learn the various ways you can use lookarounds (nesting them, using them to capture, etc):
Upvotes: 141
Reputation: 75222
Assuming you only want to disallow strings that match the regex completely (i.e., mmbla
is okay, but mm
isn't), this is what you want:
^(?!(?:m{2}|t)$).*$
(?!(?:m{2}|t)$)
is a negative lookahead; it says "starting from the current position, the next few characters are not mm
or t
, followed by the end of the string." The start anchor (^
) at the beginning ensures that the lookahead is applied at the beginning of the string. If that succeeds, the .*
goes ahead and consumes the string.
FYI, if you're using Java's matches()
method, you don't really need the the ^
and the final $
, but they don't do any harm. The $
inside the lookahead is required, though.
Upvotes: 66