regex - operator - Regular expression to match a line that doesn't contain a word?




regex not operator (18)

Benchmarks

I decided to evaluate some of the presented Options and compare their performance, as well as use some new Features. Benchmarking on .NET Regex Engine: http://regexhero.net/tester/

Benchmark Text:

The first 7 lines should not match, since they contain the searched Expression, while the lower 7 lines should match!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Results:

Results are Iterations per second as the median of 3 runs - Bigger Number = Better

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Since .NET doesn't support action Verbs (*FAIL, etc.) I couldn't test the solutions P1 and P2.

Summary:

I tried to test most proposed solutions, some Optimizations are possible for certain words. For Example if the First two letters of the search string are not the Same, answer 03 can be expanded to ^(?>[^R]+|R+(?!egex Hero))*$ resulting in a small performance gain.

But the overall most readable and performance-wise fastest solution seems to be 05 using a conditional statement or 04 with the possesive quantifier. I think the Perl solutions should be even faster and more easily readable.

I know it's possible to match a word and then reverse the matches using other tools (e.g. grep -v). However, I'd like to know if it's possible to match lines that don't contain a specific word (e.g. hede) using a regular expression.

Input:

hoho
hihi
haha
hede

Code:

grep "<Regex for 'doesn't contain hede'>" input

Desired output:

hoho
hihi
haha

How to use PCRE's backtracking control verbs to match a line not containing a word

Here's a method that I haven't seen used before:

/.*hede(*COMMIT)^|/

How it works

First, it tries to find "hede" somewhere in the line. If successful, at this point, (*COMMIT) tells the engine to, not only not backtrack in the event of a failure, but also not to attempt any further matching in that case. Then, we try to match something that cannot possibly match (in this case, ^).

If a line does not contain "hede" then the second alternative, an empty subpattern, successfully matches the subject string.

This method is no more efficient than a negative lookahead, but I figured I'd just throw it on here in case someone finds it nifty and finds a use for it for other, more interesting applications.


Answer:

^((?!hede).)*$

Explanation:

^the beginning of the string, ( group and capture to \1 (0 or more times (matching the most amount possible)),
(?! look ahead to see if there is not,

hede your string,

) end of look-ahead, . any character except \n,
)* end of \1 (Note: because you are using a quantifier on this capture, only the LAST repetition of the captured pattern will be stored in \1)
$ before an optional \n, and the end of the string


Through PCRE verb (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

This would completely skips the line which contains the exact string hede and matches all the remaining lines.

DEMO

Execution of the parts:

Let us consider the above regex by splitting it into two parts.

  1. Part before the | symbol. Part shouldn't be matched.

    ^hede$(*SKIP)(*F)
    
  2. Part after the | symbol. Part should be matched.

    ^.*$
    

PART 1

Regex engine will start its execution from the first part.

^hede$(*SKIP)(*F)

Explanation:

  • ^ Asserts that we are at the start.
  • hede Matches the string hede
  • $ Asserts that we are at the line end.

So the line which contains the string hede would be matched. Once the regex engine sees the following (*SKIP)(*F) (Note: You could write (*F) as (*FAIL)) verb, it skips and make the match to fail. | called alteration or logical OR operator added next to the PCRE verb which inturn matches all the boundaries exists between each and every character on all the lines except the line contains the exact string hede. See the demo here. That is, it tries to match the characters from the remaining string. Now the regex in the second part would be executed.

PART 2

^.*$

Explanation:

  • ^ Asserts that we are at the start. ie, it matches all the line starts except the one in the hede line. See the demo here.
  • .* In the Multiline mode, . would match any character except newline or carriage return characters. And * would repeat the previous character zero or more times. So .* would match the whole line. See the demo here.

    Hey why you added .* instead of .+ ?

    Because .* would match a blank line but .+ won't match a blank. We want to match all the lines except hede , there may be a possibility of blank lines also in the input . so you must use .* instead of .+ . .+ would repeat the previous character one or more times. See .* matches a blank line here.

  • $ End of the line anchor is not necessary here.


Aforementioned (?:(?!hede).)* is great because it can be anchored.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

But the following would suffice in this case:

^(?!.*hede)                    # A line without hede

This simplification is ready to have "AND" clauses added:

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

FWIW, since regular languages (aka rational languages) are closed under complementation, it's always possible to find a regular expression (aka rational expression) that negates another expression. But not many tools implement this.

Vcsn supports this operator (which it denotes {c}, postfix).

You first define the type of your expressions: labels are letter (lal_char) to pick from a to z for instance (defining the alphabet when working with complementation is, of course, very important), and the "value" computed for each word is just a Boolean: true the word is accepted, false, rejected.

In Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹

then you enter your expression:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

convert this expression to an automaton:

In [7]: a = e.automaton(); a

finally, convert this automaton back to a simple expression.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

where + is usually denoted |, \e denotes the empty word, and [^] is usually written . (any character). So, with a bit of rewriting ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

You can see this example here, and try Vcsn online there.


Here's how I'd do it:

^[^h]*(h(?!ede)[^h]*)*$

Accurate and more efficient than the other answers. It implements Friedl's "unrolling-the-loop" efficiency technique and requires much less backtracking.


I don't understand the need for complex regex or even lookaheads here :

/hede|^(.*)$/gm

Don't put in a capturing group the thing you don't want, but use one for everything else. This will match all lines that don't contain "hede".


If you want to match a character to negate a word similar to negate character class:

For example, a string:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

Do not use:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

Use:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

Notice "(?!bbb)." is neither lookbehind nor lookahead, it's lookcurrent, for example:

"(?=abc)abcde", "(?!abc)abcde"

It may be more maintainable to two regexes in your code, one to do the first match, and then if it matches run the second regex to check for outlier cases you wish to block for example ^.*(hede).* then have appropriate logic in your code.

OK, I admit this is not really an answer to the posted question posted and it may also use slightly more processing than a single regex. But for developers who came here looking for a fast emergency fix for an outlier case then this solution should not be overlooked.


Not regex, but I've found it logical and useful to use serial greps with pipe to eliminate noise.

eg. search an apache config file without all the comments-

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

and

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

The logic of serial grep's is (not a comment) and (matches dir)


Note that the solution to does not start with “hede”:

^(?!hede).*$

is generally much more efficient than the solution to does not contain “hede”:

^((?!hede).)*$

The former checks for “hede” only at the input string’s first position, rather than at every position.


Since the introduction of ruby-2.4.1, we can use the new Absent Operator in Ruby’s Regular Expressions

from the official doc

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

Thus, in your case ^(?~hede)$ does the job for you

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

The TXR Language supports regex negation.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

A more complicated example: match all lines that start with a and end with z, but do not contain the substring hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

Regex negation is not particularly useful on its own but when you also have intersection, things get interesting, since you have a full set of boolean set operations: you can express "the set which matches this, except for things which match that".


The below function will help you get your desired output

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

The given answers are perfectly fine, just an academic point:

Regular Expressions in the meaning of theoretical computer sciences ARE NOT ABLE do it like this. For them it had to look something like this:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

This only does a FULL match. Doing it for sub-matches would even be more awkward.


With ConyEdit, you can use the command line cc.gl !/hede/ to get lines that do not contain the regex matching, or use the command line cc.dl /hede/ to delete lines that contain the regex matching. They have the same result.


With negative lookahead, regular expression can match something not contains specific pattern. This is answered and explained by Bart Kiers. Great explanation!

However, with Bart Kiers' answer, the lookahead part will test 1 to 4 characters ahead while matching any single character. We can avoid this and let the lookahead part check out the whole text, ensure there is no 'hede', and then the normal part (.*) can eat the whole text all at one time.

Here is the improved regex:

/^(?!.*?hede).*$/

Note the (*?) lazy quantifier in the negative lookahead part is optional, you can use (*) greedy quantifier instead, depending on your data: if 'hede' does present and in the beginning half of the text, the lazy quantifier can be faster; otherwise, the greedy quantifier be faster. However if 'hede' does not present, both would be equal slow.

Here is the demo code.

For more information about lookahead, please check out the great article: Mastering Lookahead and Lookbehind.

Also, please check out RegexGen.js, a JavaScript Regular Expression Generator that helps to construct complex regular expressions. With RegexGen.js, you can construct the regex in a more readable way:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);




regex-group