How to negate the whole regex?
Asked Answered
I

6

141

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.

Incommutable answered 14/4, 2010 at 13:25 Comment(2)
As an aside, {1} is completely useless. (If you do think it provides some value, why don't you write ((m{1}a{1}){1}|(t){1}){1}?)Mayramays
what he meant to say is try {1,}Incensory
N
135

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.

Links to regular-expressions.info

See also

More examples

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):

Noaccount answered 14/4, 2010 at 13:31 Comment(4)
regular-expressions.info is a damn good resource for all things regex.Parmentier
What all have lookaround support? Does not work with grep.Discourse
Pattern.compile("(?!(a.*b))").matcher("xab").matches() should be true, right?Failing
I seems like this not right, see #8611243 for a correct alternative.Failing
S
66

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.

Stalky answered 14/4, 2010 at 13:52 Comment(3)
Most helpful part of this answer is that you have to add .* to the end of your regex, otherwise it will reject every string.Subsocial
The $ inside the negative lookahead, AND the .* at the end are both critical bits. As always with REs, a strong set of unit tests is absolutely critical to getting it right. This answer is 100% correct.Progression
This only works if the match can only occur at the end of the string. If it can occur anywhere, you need ^(?!.*(?:m{2}|t)).*$ (replace . with [\s\S] for regex flavors/flags where . doesn't match line-breaking chars).Coburg
G
2
\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

Gwenni answered 8/10, 2017 at 8:1 Comment(0)
C
0

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'] },
]

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)
}
Coburg answered 22/1 at 6:38 Comment(0)
C
-1

This regexp math your condition:

^.*(?<!ma|t)$

Look at how it works: https://regex101.com/r/Ryg2FX/1

Christan answered 19/11, 2022 at 22:21 Comment(0)
T
-2

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'
Tubulate answered 25/8, 2020 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.