The examples provided are incorrect. Most match line 4, and all match line 5. They're right that you need word boundaries and a negative lookahead, but they are missing a crucial piece.
Regex matches word boundaries before and after dots. Meaning if the third number has a decimal, the search will not run to the end of the line, will not see the final x, but will match the string.
As a solution you need to use this negative lookahead that checks after the decimal for an x: (?!\.?\d?x)
As well as wrapping the search in word boundaries: \b...\b
I tested the string below and it works.
\bRHS \d+\.?\d?x\d+\.?\d?x\d+\.?\d?(?!\.?\d?x)\b
Example: https://regex101.com/r/0nzHkN/2/
Answer from Josh Bradley on Stack OverflowThe examples provided are incorrect. Most match line 4, and all match line 5. They're right that you need word boundaries and a negative lookahead, but they are missing a crucial piece.
Regex matches word boundaries before and after dots. Meaning if the third number has a decimal, the search will not run to the end of the line, will not see the final x, but will match the string.
As a solution you need to use this negative lookahead that checks after the decimal for an x: (?!\.?\d?x)
As well as wrapping the search in word boundaries: \b...\b
I tested the string below and it works.
\bRHS \d+\.?\d?x\d+\.?\d?x\d+\.?\d?(?!\.?\d?x)\b
Example: https://regex101.com/r/0nzHkN/2/
Use this regular expression instead:
RHS \d+.?\d?x\d+.?\d?x\d+.?\d?(?!x)
Or a compact version of it:
RHS (\d+.?\d?){3}(?!x)
regex - Find 'word' not followed by a certain character - Stack Overflow
Regex match pattern not preceded by either of two expressions
Are they actually the same length as foo and bar are - because it should work in that case.
>>> import re
>>> text = 'foo bar\nbaz bar\nblub bar'
>>> re.findall('\S+(?<!foo|baz) bar', text)
['blub bar']The 3rd party regex module does support variable length (among other things)
>>> import regex as re
>>> re.findall('\S+(?<!foo|blub) bar', text)
['baz bar']Alternative you could just capture all the matches and do the filtering yourself in code.
More on reddit.compython - Regular expression to search for a string1 that is never followed by string2 - Stack Overflow
regex - python 3 find a string that is not followed by a substring - Stack Overflow
Use negative look-arounds:
re.findall(r"(?<![\da-z])[a-z]+(?![\da-z])", string.lower())
This matches lower-case letters that are not immediately preceded or followed by more letters or digits.
Demo:
>>> import re
>>> string = "ts0060_LOD-70234_lr2_billboards_rgba_over_s3d_lf_v5_2Kdciufa_lnh"
>>> re.findall(r"(?<![\da-z])[a-z]+(?![\da-z])", string.lower())
['lod', 'billboards', 'rgba', 'over', 'lf', 'lnh']
An alternative to using findall is to split the string into individual words, and then filter out any words containing non-alphabetical characters.
import re
string = "ts0060_LOD-70234_lr2_billboards_rgba_over_s3d_lf_v5_2Kdciufa_lnh"
#split on non-alphanumeric characters
words = re.split("[^a-z0-9]", string.lower())
print "words:", words
filtered_words = filter(str.isalpha, words)
print "filtered words:", filtered_words
Result:
words: ['ts0060', 'lod', '70234', 'lr2', 'billboards', 'rgba', 'over', 's3d', 'lf', 'v5', '2kdciufa', 'lnh']
filtered words: ['lod', 'billboards', 'rgba', 'over', 'lf', 'lnh']
I'm a RegEx Noob and I'm using the regex (i.e. not re, for all that matters) to match a string not preceded by either of two expression.
So the regex should not match
foo bar baz bar
but should match bar in
blub bar
I know how to match a string not preceded by something. That would be:
(?<!foo )bar
But I can't use
(?<!foo|baz )bar
because Python doesn't support variable width lookbehinds. From StackOverflow I learned that I could invert my string and and use a negative lookahead, but isn't there a less weird solution in my case?
oof(?! rab| zad)
Are they actually the same length as foo and bar are - because it should work in that case.
>>> import re
>>> text = 'foo bar\nbaz bar\nblub bar'
>>> re.findall('\S+(?<!foo|baz) bar', text)
['blub bar']
The 3rd party regex module does support variable length (among other things)
>>> import regex as re
>>> re.findall('\S+(?<!foo|blub) bar', text)
['baz bar']
Alternative you could just capture all the matches and do the filtering yourself in code.
in addition to excellent points made by commandlineluser, you can use multiple look-behinds with re module when variable-length is required... for ex: re.search(r'(?<!foo)(?<!bigwordhere)bar', s) would be same as regex.search(r'(?<!foo|bigwordhere)bar', s)
You need (?![.\d]) lookahead:
r"^\d+(?![.\d])"
See the regex demo. Details:
^- start of string\d+- 1+ digits(?![.\d])- no dot and any other digits are allowed to the right of the current location.
See the Python demo:
import re
l = ["42. blabla \n foo", "42 blabla \n foo", "422. blabla \n foo"]
regex_header = re.compile(r"^[0-9]+(?![.\d])")
for s in l:
if (regex_header.search(s)):
print(s)
# => "42 blabla \n foo"
My guess is that maybe this might be what we might want to output:
import re
list = ["42. blabla \n foo", "42 blabla \n foo", "422. blabla \n foo"]
regex_header = re.compile("^[0-9]+(?!\.)\D*$")
for str in list:
print(re.findall(regex_header, str))
Demo
Answer from The fourth bird definitely works and it is well explained as well.
As an alternative to using word boundary one can use possessive quantifier i.e. ++ to turn off backtracking thus improving efficiency further.
\$this->\w++(?!\()
RegEx Demo
Please note use of \w instead of equivalent [a-zA-Z0-9_] here.
Like a greedy quantifier, a possessive quantifier repeats the token as many times as possible. Unlike a greedy quantifier, it does not give up matches as the engine backtracks.
The (?<!\() will always be true as the character class does not match a (
Note that you don't have to escape the \_
You can use a word boundary after the character class to prevent backtracking, and turn the negative lookbehind into a negative lookahead (?!\() to assert not ( directly to the right.
\$this->[a-zA-Z0-9_]+\b(?!\()
Regex demo
You don't say what flavor of regex you're using, but this should work in general:
Tom(?!\s+Thumb)
In case you are not looking for whole words, you can use the following regex:
Tom(?!.*Thumb)
If there are more words to check after a wanted match, you may use
Tom(?!.*(?:Thumb|Finger|more words here))
Tom(?!.*Thumb)(?!.*Finger)(?!.*more words here)
To make . match line breaks please refer to How do I match any character across multiple lines in a regular expression?
See this regex demo
If you are looking for whole words (i.e. a whole word Tom should only be matched if there is no whole word Thumb further to the right of it), use
\bTom\b(?!.*\bThumb\b)
See another regex demo
Note that:
\b- matches a leading/trailing word boundary(?!.*Thumb)- is a negative lookahead that fails the match if there are any 0+ characters (depending on the engine including/excluding linebreak symbols) followed withThumb.