Regular expression to find syllables in Bengali word - python

Here is the code:
BanglaAlphabet = {
'Consonant' : '[\u0995-\u09B9\u09CE\u09DC-\u09DF]',
'IndependantVowel' : '[\u0985-\u0994]',
'DependantVowel' : '[\u09BE-\u09CC\u09D7]',
'Nukta' : '[\u09BC]'
}
BanglaWordPattern = ur"""(
({DependantVowel}{Nukta}{Consonant}) |
({DependantVowel}{Consonant}) |
{IndependantVowel} |
{Consonant} |
)+""".format(**BanglaAlphabet)
BanglaWordPattern = re.compile(BanglaWordPattern, re.VERBOSE)
The matching is done with:
re.match(BanglaWordPattern, w[::-1])
This is meant to match a valid Bengali word when matched from right to left.
However, it is matching invalid words, such as োগাড় and িদগ.
What could be the problem?
Edit
After numerous corrections as suggested by #GarethRees and #ChrisMorgan, I ended up with:
bangla_alphabet = dict(
consonant = u'[\u0995-\u09b9\u09ce\u09dc-\u09df]',
independent_vowel = u'[\u0985-\u0994]',
dependent_vowel = u'[\u09be-\u09cc\u09d7]',
dependent_sign = u'[\u0981-\u0983\u09cd]',
virama = u'[\u09cd]'
)
bangla_word_pattern = re.compile(ur'''(?:
{consonant}
({virama}{consonant})?
({virama}{consonant})?
{dependent_vowel}?
{dependent_sign}?
|
{independent_vowel}
{dependent_sign}?
)+
The matching is now:
bangla_word_pattern.match(w)
This code not only corrects errors, but accounts for more characters and valid constructs than before.
I am happy to report that it is working as expected. As such, this code now serves as a very basic regular expression for validating the syntax of Bengali words.
There are several special rules / exceptions not implemented. I will be looking into those and adding them to this basic structure incrementally.
Many ''.format(**bangla_alphabet), re.VERBOSE)
The matching is now:
xCodexBlockxPlacexHolderx
This code not only corrects errors, but accounts for more characters and valid constructs than before.
I am happy to report that it is working as expected. As such, this code now serves as a very basic regular expression for validating the syntax of Bengali words.
There are several special rules / exceptions not implemented. I will be looking into those and adding them to this basic structure incrementally.

Your string কয়া is made up of these characters:
>>> import unicodedata
>>> map(unicodedata.name, u'কয়া')
['BENGALI LETTER KA', 'BENGALI LETTER YA', 'BENGALI SIGN NUKTA', 'BENGALI VOWEL SIGN AA']
U+09BC BENGALI SIGN NUKTA is not matched by your regular expression.
Looking at the Bengali code chart it seems possible that you missed some other characters.
OK, to answer your updated question. You are making three mistakes:
Your strings in the BanglaAlphabet dictionary are lacking the u (Unicode) flag. This means that Unicode escape sequences like \u0995 are not being translated into Unicode characters. You just get backslashes, letters, and digits.
In the BanglaWordPattern regular expression, there is a vertical bar | near the end, with nothing after it. That means the whole regular expression looks like (stuff1|stuff2|stuff3|stuff4|)+. So there are really five alternatives, the last one being empty. The empty regular expression matches anything, of course.
You are not actually looking at the result of your program to see what it actually matched. If you write m = re.match(BanglaWordPattern, w[::-1]); print m.group(0) you'll see that what actually matched was the empty string.
I think the following are also mistakes, but you haven't explained what you are trying to do, so I'm not so confident:
You are doing the match backwards, which is unnecessary. It would be simpler and easier to understand if you turned your patterns round and matched forwards.
You are using capturing parentheses in your regular expressions. If you don't need the results, use non-capturing parentheses (?:...) instead.
The inner sets of parentheses are unnecessary anyway.
You are not anchoring the end of your regular expression at a word boundary or the end of the string.
I would write something like this:
import re
bangla_categories = dict(
consonant = u'[\u0995-\u09B9\u09CE\u09DC-\u09DF]',
independent_vowel = u'[\u0985-\u0994]',
dependent_vowel = u'[\u09BE-\u09CC\u09D7]',
nukta = u'[\u09BC]',
)
bangla_word_re = re.compile(ur"""(?:
{consonant}{nukta}{dependent_vowel} |
{consonant}{dependent_vowel} |
{independent_vowel} |
{consonant}
)+(?:\b|$)""".format(**bangla_categories), re.VERBOSE)
But I would also look at the other Bangla signs in the code charts that you've omitted. What about U+0981 BENGALI SIGN CANDRABINDU and U+0982 BENGALI SIGN ANUSVARA (which nasalise vowels)? What about U+09CD BENGALI SIGN VIRAMA (which cancels a vowel)? And so on.

There are a few issues with what you've got:
Your regular expressions end up including the literal \u0995 etc. in them, because they're not Unicode strings; you need to include the actual Unicode character.
You want $ at the end of the regular expression so that it will only be matching the whole string.
You had an empty string in your group as valid (by ending the first group with a pipe, leaving an empty option). This, in combination with the lack of a $ symbol, meant that it wouldn't work.
It's not complete (as observed by Gareth).
Also be aware that you can also do bengali_word_pattern.match(s) instead of re.match(bengali_word_pattern, s) once you've got a compiled regular expression object.
bengali_alphabet = {
'consonant': u'[\u0995-\u09B9\u09CE\u09DC-\u09DF]',
'independent_vowel': u'[\u0985-\u0994]',
'dependent_vowel': u'[\u09BE-\u09CC\u09D7]',
'nukta': u'\u09BC'
}
bengali_word_pattern = ur'''^(?:
(?:{dependent_vowel}{nukta}{consonant}) |
(?:{dependent_vowel}{consonant}) |
{independent_vowel} |
{consonant}
)+$'''.format(**bengali_alphabet)
bengali_word_pattern = re.compile(bengali_word_pattern, re.VERBOSE)
Now,
>>> bengali_word_pattern.match(u'বাংলা'[::-1])
This one doesn't work because of the "ং" character, U+0982; it's not in any of your ranges. Not sure what category that bit falls into off-hand; If we just take out the offending character it works. (Google Translate tells me that the resulting word could be translated "bracelet"—I don't know, I'd need to ask my sister; approximately all I can truthfully say is আমি বাংলা বলতে পারি না. Almost all I know is convenient everyday phrases like মুরগি চোর. And the first word of that contains a vowel missed thus far, too. Anyway, that's beside the point.)
>>> bengali_word_pattern.match(u'বালা')
<_sre.SRE_Match object at 0x7f00f5bf9620>
It works on the "chicken thief" phrase, too.
>>> [bengali_word_pattern.match(w[::-1]) for w in u'মুরগি চোর'.split()]
[<_sre.SRE_Match object at 0x7f00f5bf9620>, <_sre.SRE_Match object at 0x7f00f5bf97e8>]
And it doesn't give a match for those two examples of incomplete words:
>>> bengali_word_pattern.match(u'োগাড়'[::-1])
>>> bengali_word_pattern.match(u'িদগ'[::-1])
I will also at this point admit myself puzzled as to why you are parsing the strings backwards; I would have thought it would make sense for it to be done forwards (this regular expression works correctly, then you don't need to use [::-1]):
^(?:
{consonant}
(?:
{nukta}?
{dependent_vowel}
)?
|
{independent_vowel}
)+$
At each conjunct/letter, get either an independent vowel or a consonant possibly followed by a dependent vowel, with a nukta possibly between them.
Other alterations that I have made:
Variable/item naming, to fit in with standard Python coding style;
Replaced (...) (matching group) with (?:...) (non-matching group) for performance (see docs);
Corrected the spelling of "dependent";
Changed "bangla" to "bengali" as in English it is Bengali; I prefer when speaking English to use the standard English name for a language rather than the native language pronunciation, Anglicised if necessary—e.g. French rather than le français. On the other hand, I do realise that Bengali is regularly called Bangla by English speakers.

Related

capture anything but string [duplicate]

I know it's possible to match a word and then reverse the matches using other tools (e.g. grep -v). However, is it possible to match lines that do not 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
The notion that regex doesn't support inverse matching is not entirely true. You can mimic this behavior by using negative look-arounds:
^((?!hede).)*$
The regex above will match any string, or line without a line break, not containing the (sub)string 'hede'. As mentioned, this is not something regex is "good" at (or should do), but still, it is possible.
And if you need to match line break chars as well, use the DOT-ALL modifier (the trailing s in the following pattern):
/^((?!hede).)*$/s
or use it inline:
/(?s)^((?!hede).)*$/
(where the /.../ are the regex delimiters, i.e., not part of the pattern)
If the DOT-ALL modifier is not available, you can mimic the same behavior with the character class [\s\S]:
/^((?!hede)[\s\S])*$/
Explanation
A string is just a list of n characters. Before, and after each character, there's an empty string. So a list of n characters will have n+1 empty strings. Consider the string "ABhedeCD":
┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
└──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘
index 0 1 2 3 4 5 6 7
where the e's are the empty strings. The regex (?!hede). looks ahead to see if there's no substring "hede" to be seen, and if that is the case (so something else is seen), then the . (dot) will match any character except a line break. Look-arounds are also called zero-width-assertions because they don't consume any characters. They only assert/validate something.
So, in my example, every empty string is first validated to see if there's no "hede" up ahead, before a character is consumed by the . (dot). The regex (?!hede). will do that only once, so it is wrapped in a group, and repeated zero or more times: ((?!hede).)*. Finally, the start- and end-of-input are anchored to make sure the entire input is consumed: ^((?!hede).)*$
As you can see, the input "ABhedeCD" will fail because on e3, the regex (?!hede) fails (there is "hede" up ahead!).
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.
If you're just using it for grep, you can use grep -v hede to get all lines which do not contain hede.
ETA Oh, rereading the question, grep -v is probably what you meant by "tools options".
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
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.
If you want the regex test to only fail if the entire string matches, the following will work:
^(?!hede$).*
e.g. -- If you want to allow all values except "foo" (i.e. "foofoo", "barfoo", and "foobar" will pass, but "foo" will fail), use: ^(?!foo$).*
Of course, if you're checking for exact equality, a better general solution in this case is to check for string equality, i.e.
myStr !== 'foo'
You could even put the negation outside the test if you need any regex features (here, case insensitivity and range matching):
!/^[a-f]oo$/i.test(myStr)
The regex solution at the top of this answer may be helpful, however, in situations where a positive regex test is required (perhaps by an API).
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 a good explanation of why it's not easy to negate an arbitrary regex. I have to agree with the other answers, though: if this is anything other than a hypothetical question, then a regex is not the right choice here.
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()
);
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: ^(?!.*?Regex Hero).* 7.356 // Lookahead at the beginning, if not found match everything
04: ^(?>[^R]+|R(?!egex Hero))*$ 6.137 // Lookahead only on the right first letter
05: ^(?>(?:.*?Regex Hero)?)^.*$ 7.426 // Match the word and check if you're still at linestart
06: ^(?(?=.*?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:
The overall most readable and performance-wise fastest solution seems to be 03 with a simple negative lookahead. This is also the fastest solution for JavaScript, since JS does not support the more advanced Regex Features for the other solutions.
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)
Since no one else has given a direct answer to the question that was asked, I'll do it.
The answer is that with POSIX grep, it's impossible to literally satisfy this request:
grep "<Regex for 'doesn't contain hede'>" input
The reason is that with no flags, POSIX grep is only required to work with Basic Regular Expressions (BREs), which are simply not powerful enough for accomplishing that task, because of lack of alternation in subexpressions. The only kind of alternation it supports involves providing multiple regular expressions separated by newlines, and that doesn't cover all regular languages, e.g. there's no finite collection of BREs that matches the same regular language as the extended regular expression (ERE) ^(ab|cd)*$.
However, GNU grep implements extensions that allow it. In particular, \| is the alternation operator in GNU's implementation of BREs. If your regular expression engine supports alternation, parentheses and the Kleene star, and is able to anchor to the beginning and end of the string, that's all you need for this approach. Note however that negative sets [^ ... ] are very convenient in addition to those, because otherwise, you need to replace them with an expression of the form (a|b|c| ... ) that lists every character that is not in the set, which is extremely tedious and overly long, even more so if the whole character set is Unicode.
Thanks to formal language theory, we get to see how such an expression looks like. With GNU grep, the answer would be something like:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(found with Grail and some further optimizations made by hand).
You can also use a tool that implements EREs, like egrep, to get rid of the backslashes, or equivalently, pass the -E flag to POSIX grep (although I was under the impression that the question required avoiding any flags to grep whatsoever):
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Here's a script to test it (note it generates a file testinput.txt in the current directory). Several of the expressions presented in other answers fail this test.
#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"
# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede
h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)
In my system it prints:
Files /dev/fd/63 and /dev/fd/62 are identical
as expected.
For those interested in the details, the technique employed is to convert the regular expression that matches the word into a finite automaton, then invert the automaton by changing every acceptance state to non-acceptance and vice versa, and then converting the resulting FA back to a regular expression.
As everyone has noted, if your regular expression engine supports negative lookahead, the regular expression is much simpler. For example, with GNU grep:
grep -P '^((?!hede).)*$' input
However, this approach has the disadvantage that it requires a backtracking regular expression engine. This makes it unsuitable in installations that are using secure regular expression engines like RE2, which is one reason to prefer the generated approach in some circumstances.
Using Kendall Hopkins' excellent FormalTheory library, written in PHP, which provides a functionality similar to Grail, and a simplifier written by myself, I've been able to write an online generator of negative regular expressions given an input phrase (only alphanumeric and space characters currently supported, and the length is limited): http://www.formauri.es/personal/pgimeno/misc/non-match-regex/
For hede it outputs:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
which is equivalent to the above.
with this, you avoid to test a lookahead on each positions:
/^(?:[^h]+|h++(?!ede))*+$/
equivalent to (for .net):
^(?>(?:[^h]+|h+(?!ede))*)$
Old answer:
/^(?>[^h]+|h+(?!ede))*$/
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
An, in my opinon, more readable variant of the top answer:
^(?!.*hede)
Basically, "match at the beginning of the line if and only if it does not have 'hede' in it" - so the requirement translated almost directly into regex.
Of course, it's possible to have multiple failure requirements:
^(?!.*(hede|hodo|hada))
Details: The ^ anchor ensures the regex engine doesn't retry the match at every location in the string, which would match every string.
The ^ anchor in the beginning is meant to represent the beginning of the line. The grep tool matches each line one at a time, in contexts where you're working with a multiline string, you can use the "m" flag:
/^(?!.*hede)/m # JavaScript syntax
or
(?m)^(?!.*hede) # Inline flag
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.
Another option is that to add a positive look-ahead and check if hede is anywhere in the input line, then we would negate that, with an expression similar to:
^(?!(?=.*\bhede\b)).*$
with word boundaries.
The expression is explained on the top right panel of regex101.com, if you wish to explore/simplify/modify it, and in this link, you can watch how it would match against some sample inputs, if you like.
RegEx Circuit
jex.im visualizes regular expressions:
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"
The OP did not specify or Tag the post to indicate the context (programming language, editor, tool) the Regex will be used within.
For me, I sometimes need to do this while editing a file using Textpad.
Textpad supports some Regex, but does not support lookahead or lookbehind, so it takes a few steps.
If I am looking to retain all lines that Do NOT contain the string hede, I would do it like this:
1. Search/replace the entire file to add a unique "Tag" to the beginning of each line containing any text.
Search string:^(.)
Replace string:<##-unique-##>\1
Replace-all
2. Delete all lines that contain the string hede (replacement string is empty):
Search string:<##-unique-##>.*hede.*\n
Replace string:<nothing>
Replace-all
3. At this point, all remaining lines Do NOT contain the string hede. Remove the unique "Tag" from all lines (replacement string is empty):
Search string:<##-unique-##>
Replace string:<nothing>
Replace-all
Now you have the original text with all lines containing the string hede removed.
If I am looking to Do Something Else to only lines that Do NOT contain the string hede, I would do it like this:
1. Search/replace the entire file to add a unique "Tag" to the beginning of each line containing any text.
Search string:^(.)
Replace string:<##-unique-##>\1
Replace-all
2. For all lines that contain the string hede, remove the unique "Tag":
Search string:<##-unique-##>(.*hede)
Replace string:\1
Replace-all
3. At this point, all lines that begin with the unique "Tag", Do NOT contain the string hede. I can now do my Something Else to only those lines.
4. When I am done, I remove the unique "Tag" from all lines (replacement string is empty):
Search string:<##-unique-##>
Replace string:<nothing>
Replace-all
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"]
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.
Part before the | symbol. Part shouldn't be matched.
^hede$(*SKIP)(*F)
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.
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".
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.
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;
}
?>
I wanted to add another example for if you are trying to match an entire line that contains string X, but does not also contain string Y.
For example, let's say we want to check if our URL / string contains "tasty-treats", so long as it does not also contain "chocolate" anywhere.
This regex pattern would work (works in JavaScript too)
^(?=.*?tasty-treats)((?!chocolate).)*$
(global, multiline flags in example)
Interactive Example: https://regexr.com/53gv4
Matches
(These urls contain "tasty-treats" and also do not contain "chocolate")
example.com/tasty-treats/strawberry-ice-cream
example.com/desserts/tasty-treats/banana-pudding
example.com/tasty-treats-overview
Does Not Match
(These urls contain "chocolate" somewhere - so they won't match even though they contain "tasty-treats")
example.com/tasty-treats/chocolate-cake
example.com/home-cooking/oven-roasted-chicken
example.com/tasty-treats/banana-chocolate-fudge
example.com/desserts/chocolate/tasty-treats
example.com/chocolate/tasty-treats/desserts
As long as you are dealing with lines, simply mark the negative matches and target the rest.
In fact, I use this trick with sed because ^((?!hede).)*$ looks not supported by it.
For the desired output
Mark the negative match: (e.g. lines with hede), using a character not included in the whole text at all. An emoji could probably be a good choice for this purpose.
s/(.*hede)/🔒\1/g
Target the rest (the unmarked strings: e.g. lines without hede). Suppose you want to keep only the target and delete the rest (as you want):
s/^🔒.*//g
For a better understanding
Suppose you want to delete the target:
Mark the negative match: (e.g. lines with hede), using a character not included in the whole text at all. An emoji could probably be a good choice for this purpose.
s/(.*hede)/🔒\1/g
Target the rest (the unmarked strings: e.g. lines without hede). Suppose you want to delete the target:
s/^[^🔒].*//g
Remove the mark:
s/🔒//g
^((?!hede).)*$ is an elegant solution, except since it consumes characters you won't be able to combine it with other criteria. For instance, say you wanted to check for the non-presence of "hede" and the presence of "haha." This solution would work because it won't consume characters:
^(?!.*\bhede\b)(?=.*\bhaha\b)
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.
Simplest thing that I could find would be
[^(hede)]
Tested at https://regex101.com/
You can also add unit-test cases on that site
A simpler solution is to use the not operator !
Your if statement will need to match "contains" and not match "excludes".
var contains = /abc/;
var excludes =/hede/;
if(string.match(contains) && !(string.match(excludes))){ //proceed...
I believe the designers of RegEx anticipated the use of not operators.

Verbatim-like context in a regular expression

Question:
Is there any way to tell a regular expression engine to treat a certain part of a regular expression as verbatim (i.e. look for that part exactly as it is, without the usual parsing) without manually escaping special characters?
Some context:
I'm trying to backreference a group on a given regular expression from another regular expression. For instance, suppose I want to match hello(.*?)olleh against text 1 and then look for bye$1eyb in text 2, where $1 will be replaced by whatever matched group 1 in text 1. Therefore, if text 1 happens to contain the string "helloFOOolleh", the program will look for "byeFOOeyb" in text 2.
The above works fine in most cases, but if text 1 were to contain something like "hello.olleh", the program will match not only "hello.olleh" but also "helloXolleh", "hellouolleh", etc. in text 2, as it is interpreting . as a regex special character and not the plain dot character.
Additional comments:
I can't just search for the plain string resulting from parsing $1 into whatever group 1 matches, as whatever I want to search for in text 2 could itself contain other unrelated regular expressions.
I have been trying to avoid parsing the match returned from text 1 and escape every single special character, but if anyone knows of a way to do that neatly that could also work.
I'm currently working on this in Python, but if it can be done easily with any other language/program I'm happy to give it a try.
You can use the re.escape function to escape the text you want to match literally. So after you extract your match text (e.g., "." in "hello.olleh"), apply re.escape to it before inserting it into your second regex.
To illustrate what BrenBarn wrote,
import re
text1 = "hello.olleh"
text2_match = "bye.eyb"
text2_nomatch = "byeXeyb"
found = re.fullmatch(r"hello(.*?)olleh", text1).group(1)
You can then make a new search with the re.escape:
new_search = "bye{}eyb".format(re.escape(found))
Tests:
re.search(new_search, text2_match)
#>>> <_sre.SRE_Match object; span=(0, 7), match='bye.eyb'>
re.search(new_search, text2_nomatch)
#>>> None

using regular expression to find the urls that do not contain specific word in domain part

I want a regular expression to grab urls that does not contain specific word in their domain name but no matter if there is that word in the query string or other subdirectories of the domain.Also it doesn't matter how the hrl starts for exmaple by http/fttp/https/without any of them. I found this expression ^((?!foo).)*$") I don't know how should I change it to fit into these conditions.
These are the accepted url for the word "foo":
whatever.whatever.whatever/foo/pic
whatever.whatever.whatever?sdfd="foo"
and these are not accepted:
whatever.whateverfoo.whatever
whatever.foowhatever.whatever
whatever.foo.whatever.whatever
whatever.whatever.foo.whatever
Try this (explanation):
^(?:(?!foo).)*?[\/\?]
What this means is basically:
match anthing not containing foo
until a slash or question mark is encountered
The precise syntax may vary depending on your programming language/editor. The explanation link shows the PHP example. The regex elements I've used are pretty common, so it should work for you. If not, let me know.
This regex can only be matched against a single URL at a time. So if you are trying this in regex101, don't enter all URLs at once.
Update: Example in Java (now using turner instead of foo):
Pattern p = Pattern.compile("^(?:(?!turner).)*?[\\/\\?].*");
System.out.println(p.matcher(
"i.cdn.turner.com/cnn/.e/img/3.0/1px.gif").matches());
System.out.println(p.matcher(
"www.facebook.com/plugins/like.php?href=http%3A%2F%2F"
+ "www.facebook.com%2Fturnerkjl‌​jl").matches());
Output:
false
true
Here is your regex in java
"^[^/?]+(?<!foo)"
Explanation - From beginning search for characters which does not matches with / or ?. The moment it finds any one of the above two characters then the pattern search backward for negative match for foo. If foo is found then it returns false else true. This is in java. Also the regex will vary from language to language.
in grep cmd (unix or shell script) you have to take negation of the following regex match
"^[^/?]+foo"
Here's a regex that will match the cases that you want to reject
(?:.+://){0,1}(?<subdomain>[^.]+\.){0,1}(?<domain>[^.]*whatever[^.]*\.)(?<top>[^.]+).*
(?: ) is a non-capturing group
(?<groupName> ) is a named group (useful for testing, in regexhero you can see what is being captured by the group)
{0,1} means 0 or 1
. means any character except new line
[^.] means any character except "."
means 0 or more
means 1 or more, for example, .+ means 1 or many "any characters"
. escapes the special character .
See http://www.mikesdotnetting.com/Article/46/CSharp-Regular-Expressions-Cheat-Sheet
you can try it here: http://regexhero.net/tester/

Python findall, regexp with "|" and grouping

I'm trying to implement Pig Latin with Python.
I want to match strings which begins by a consonant or "qu" (no matter the case) so to find the first letters, so at first I was doing :
first_letters = re.findall(r"^[^aeiou]+|^[qQ][uU]", "qualification")
It didn't work (finds only "q") so I figured that i had to add the q in the first group :
first_letters = re.findall(r"^[^aeiouq]+|^[qQ][uU]", "qualification")
so that works (it finds "qu" and not only "q") !
But playing around I found myself with this :
first_letters = re.findall(r"{^[^aeiou]+}|{^[qQ][uU]}", "qualification")
that didn't work because it is the same as the first expression I tried I think.
But finally this also worked :
first_letters = re.findall(r"{^[^aeiou]+}|(^[qQ][uU])", "qualification")
and I don't know why.
Someone can tell me why ?
You should put qu before [^aeuio], because otherwise "q" gets captured by the class and fails to match. Besides that, [Qq][Uu] is not needed, just provide the case insensitive flag:
first_letters = re.findall(r"^(qu|[^aeiou]+)", "qualification", re.I)
Given that you're probably going to match the rest of the string as well, this would be more practical:
start, rest = re.findall(r"^(qu|[^aeiou]+)?(.+)", word, re.I)[0]
Reverse the order of the rules:
>>> re.findall(r"^[qQ][uU]|^[^aeiou]+", "qualification")
['qu']
>>> re.findall(r"^[qQ][uU]|^[^aeiou]+", "boogie")
['b']
>>> re.findall(r"^[qQ][uU]|^[^aeiou]+", "blogie")
['bl']
In your first case, the first regex ^[^aeiou]+ matches the q. In the second case, since you've added q to the first part, the regex engine examines the second expression and matches qu.
In your other cases, I don't think the first expression does what you think it does (i.e. the ^ character inside the braces), so it's the second expression which matches again.
The first part of your 3rd and 4th patterns, {^[^aeiou]+} is trying to match a literal curly brace { followed by a start-of-line followed by one or more non-vowel characters, followed by a literal closing curly brace }. Since you don't have re.MULTILINE enabled, I'd assume that your pattern will be technically valid but unable to match any input.
The | runs left to right, and stops at the first success. So, that's why you found only q with the first expression, and qu with the second.
Not sure what your final regex does, particularly with regard to the {} expression. The part after the | will match in qualification, though. Perhaps that's what you are seeing.
You might find the re.I (ignore case) flag useful.

Substituting a regex only when it doesn't match another regex (Python)

Long story short, I have two regex patterns. One pattern matches things that I want to replace, and the other pattern matches a special case of those patterns that should not be replace. For a simple example, imagine that the first one is "\{.*\}" and the second one is "\{\{.*\}\}". Then "{this}" should be replaced, but "{{this}}" should not. Is there an easy way to take a string and say "substitute all instances of the first string with "hello" so long as it is not matching the second string"?
In other words, is there a way to make a regex that is "matches the first string but not the second" easily without modifying the first string? I know that I could modify my first regex by hand to never match instances of the second, but as the first regex gets more complex, that gets very difficult.
Using negative look-ahead/behind assertion
pattern = re.compile( "(?<!\{)\{(?!\{).*?(?<!\})\}(?!\})" )
pattern.sub( "hello", input_string )
Negative look-ahead/behind assertion allows you to compare against more of the string, but is not considered as using up part of the string for the match. There is also a normal look ahead/behind assertion which will cause the string to match only if the string IS followed/preceded by the given pattern.
That's a bit confusing looking, here it is in pieces:
"(?<!\{)" #Not preceded by a {
"\{" #A {
"(?!\{)" #Not followed by a {
".*?" #Any character(s) (non-greedy)
"(?<!\})" #Not preceded by a } (in reference to the next character)
"\}" #A }
"(?!\})" #Not followed by a }
So, we're looking for a { without any other {'s around it, followed by some characters, followed by a } without any other }'s around it.
By using negative look-ahead/behind assertion, we condense it down to a single regular expression which will successfully match only single {}'s anywhere in the string.
Also, note that * is a greedy operator. It will match as much as it possibly can. If you use "\{.*\}" and there is more than one {} block in the text, everything between will be taken with it.
"This is some example text {block1} more text, watch me disappear {block2} even more text"
becomes
"This is some example text hello even more text"
instead of
"This is some example text hello more text, watch me disappear hello even more text"
To get the proper output we need to make it non-greedy by appending a ?.
The python docs do a good job of presenting the re library, but the only way to really learn is to experiment.
You can give replace a function (reference)
But make sure the first regex contain the second one. This is just an example:
regex1 = re.compile('\{.*\}')
regex2 = re.compile('\{\{.*\}\}')
def replace(match):
match = match.group(0)
if regex2.match(match):
return match
return 'replacement'
regex1.sub(replace, data)
You could replace all the {} instances with your replacement string (which would include the {{}} ones), and then replace the {{}} ones with a back-reference to itself (overwriting the first replace with the original data) -- then only the {} instances would have changed.
It strikes me as sub-optimal to match against two different regexes when what you're looking for is really one pattern. To illustrate:
import re
foo = "{{this}}"
bar = "{that}"
re.match("\{[^\{].*[^\}]\}", foo) # gives you nothing
re.match("\{[^\{].*[^\}]\}", bar) # gives you a match object
So it's really your regex which could be a bit more precise.

Categories