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']
Answer from Martijn Pieters on Stack OverflowUse 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)
python - regex match if not followed by pattern - Stack Overflow
python - Regular expression to search for a string1 that is never followed by string2 - Stack Overflow
Python Regex: Match a string not preceded by or followed by a word with digits in it - Stack Overflow
regex - Find 'word' not followed by a certain character - Stack Overflow
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/
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)
It can be done with Pypi regex library that supports variable-length lookbehind.
import regex
str = 'Today is 4th April. Her name is April. Tomorrow is April 5th.'
res = regex.sub(r'(?<!\d[a-z]* )April(?! [a-z]*\d)', 'PERSON', str)
print(res)
Output:
Today is 4th April. Her name is PERSON. Tomorrow is April 5th.
Explanation:
(?<!\d[a-z]* ) # negative lookbehind, make sure we haven't a digit followed by 0 or more letters and a space before
April # literally
(?! [a-z]*\d) # negative lookahead, make sure we haven't a space, 0 or more letters and a digit after
Update with re module:
import re
str = 'Today is 4th April. Her name is April. Tomorrow is April 5th.'
res = re.sub(r'(\b[a-z]+ )April(?! [a-z]*\d)', '\g<1>PERSON', str)
print(res)
This is one regex you could use:
(?:^\s+|[^\w\s]+\s*|\b[^\d\s]+\s+)(April)\b(?!\s*\w*\d)
with the case-indifferent flag set. The target word is captured in capture group 1.
Demo
Python's regex engine performs the following operations:
(?: # begin non-cap grp
^ # match beginning of line
\s* # match 0+ whitespace characters
| # or
[^\w\s]+ # match 1+ chars other than word chars and whitespace
\s* # match 0+ whitespace chars
| # or
\b # match word break
[^\d\s]+ # match 1+ chars other than digits and whitespace
\s+ # match 1+ whitespace chars
) # end non-cap grp
(April) # match 'April' in capture group
\b # match word break
(?! # begin negative lookahead
\s* # match 0+ whitespace chars
\w* # match 0+ word chars
\d # match a digit
) # end negative lookahead
The approach I've taken was to specify what may precede "April" and why may not follow it. I could not specify what cannot precede "April" as that would require a negative lookbehind, which is not supported by Python's regex engine.
I've assume that "April" may:
- be at the beginning of the string, possibly followed by spaces;
- be preceded by a character that is neither a word character nor a space, possibly followed by spaces; or
- be preceded by a word containing no digits, possibly followed by spaces.
I've also assumed that "April" is followed by a word break which may not be followed by a word containing a digit, possibly preceded by spaces.
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.
I'm a RegEx Noob and I'm using Python 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)
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