How to extract the text between asterisks except “NOT” in this string provided that regular expression can only be used?
* apple * * orange tasty * * banana * * grape NOT *
Expected results:
apple
orange tasty
banana
How to extract the text between asterisks except “NOT” in this string provided that regular expression can only be used?
* apple * * orange tasty * * banana * * grape NOT *
Expected results:
apple
orange tasty
banana
Your can use the following regular expression:
\*(\s*(?!NOT)\w+)+\s*\*
Did not read all answers, but Mr. Morton's answer is quite insightful.
The question is also very interesting with variety of ways to be approached, and we would be starting with an expression that collects our NOT words using a word boundary, altered by our desired outputs. Finally, we swipe from the start to the end of our string what might be left, especially if we wish to replace, yet this last part is unnecessary:
(?:(\bNOT\b).)*|([\w\s]+?)|(.*?)
Here, we have two undesired groups:
(?:(\bNOT\b).)*
and
(.*?)
and our desired data is in this second capturing group:
([\w\s]+?)
which can be simply modified if we have more desired chars than those listed in our char class: [\w\s].
If this expression wasn't desired and you wish to modify it, please visit this link at regex101.com.
jex.im visualizes regular expressions:
You might make use of SKIP FAIL and \K to forget what was currenly matched:
\*[^*]* NOT [^*]*\*(*SKIP)(*FAIL)|\*\s*\K\w+(?: \w+)*(?= \*)
Explanation
\*[^*]* NOT [^*]*\* Match NOT between asterix and space using a negated character class(*SKIP)(*FAIL) Skip what is matched| Or\*\s*\K Match asterix, 0+ whitespace chars and forget what was matched\w+(?: \w+)* Match 1+ word chars and a repeating pattern to match a space and 1+ word chars(?= \*) Positive lookahead, assert what is on the right is a space and an asterixFor example:
echo "* apple * * orange tasty * * banana * * grape NOT *" | grep -Po "\*[^*]* NOT [^*]*\*(*SKIP)(*FAIL)|\*\s*\K\w+(?: \w+)*(?= \*)"
Result
apple
orange tasty
banana
You can use this to also extract the other data in another group:
\*\s?(?:([a-zA-Z0-9 ]+?NOT))\s?\*|\*\s?([a-zA-Z0-9 ]+?)\s?\*
This also works in other regex engines.
Check it out here.
Visual Embeded:
<iframe frameborder="0" width="746" height="562" src="https://jex.im/regulex/#!embed=true&flags=&re=%5C*%5Cs%3F(%3F%3A(%5Ba-zA-Z0-9%20%5D%2B%3FNOT))%5Cs%3F%5C*%7C%5C*%5Cs%3F(%5Ba-zA-Z0-9%20%5D%2B%3F)%5Cs%3F%5C*"></iframe>
With GNU awk for multi-char RS and RT:
$ awk -v RS='[*][^*]+[*]' 'RT !~ /NOT/{print RT}' file
* apple *
* orange tasty *
* banana *
With any awk in any shell on every UNIX box:
$ awk '{ while ( match($0,/[*][^*]+[*]/) ) {str=substr($0,RSTART,RLENGTH); if (str !~ /NOT/) print str; $0=substr($0,RSTART+RLENGTH)} }' file
* apple *
* orange tasty *
* banana *
The above solve the problem with a couple of trivial regexps instead of one complicate regexp. It's trivial to remove the *s at each end of the output but I feel like the OP is going to want to use some convoluted single regexp instead.