Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Say I have the following string: "<hello>something WRONG</hello><hello>something else</hello>"

I am trying to find code that works like the following:

StringCases[string, 
 "<hello>" ~~ 
   something : StringPatternThatDoesntContainString["WRONG"] ~~ 
   "</hello>" :> something]

I'm trying to find all the string between hello tags that don't contain the "WRONG" string.

share|improve this question
str = "<hello>something</hello><hello>something</hello><hello>something WRONG</hello>";
StringCases[str, 
 "<hello>" ~~ Shortest[st__] ~~ "</hello>" /; StringFreeQ[st, "WRONG"] :> st]
{"something", "something"}

(With hat tip to Alexey Popkov for adding robustness via Shortest).

share|improve this answer
    
Your current solution fails with for example str = "<hello>something</hello><hello>something2</hello>";. You should replace st__ with Shortest[st__] to avoid this: by default string patterns are greedy (in contrast with the usual patterns). – Alexey Popkov Mar 28 at 12:11
    
@Alexey. Agreed. I will modify it. – march Mar 28 at 15:36
    
I have taken the liberty to update the output in your answer (which you forgot to update) and to add more representative str for testing the solution. I hope you don't mind. – Alexey Popkov Mar 28 at 16:06
    
@AlexeyPopkov. Yeah! No problem. I hadn't had my morning coffee yet. – march Mar 28 at 16:07

A solution using regular expression with Negative Lookahead (?!regex) Before the Match:

string = "<hello>something WRONG</hello><hello>something else</hello><hello>WRONG</hello>";

StringCases[string, 
 "<hello>" ~~ st : RegularExpression["(?:(?!WRONG).)*?"] ~~ "</hello>" :> st]
{"something else"}

An alternative solution using pure regexes (should be more efficient):

StringCases[string, RegularExpression["(?ms)<hello>((?:(?!WRONG).)*?)</hello>"] :> "$1"]
{"something else"}

A detailed description of this method can be found here.


UPDATE

Performance comparison of the three methods including the improved solution by march (timings for version 10.4 on Win7 x64):

string = "<hello>something WRONG</hello><hello>something</hello><hello>something \
else</hello><hello>WRONG</hello>";
stringBig = StringJoin@ConstantArray[string, 1*^3];

First@AbsoluteTiming[
  r1 = StringCases[stringBig, 
    "<hello>" ~~ Shortest[st__] ~~ "</hello>" /; StringFreeQ[st, "WRONG"] :> st]]
First@AbsoluteTiming[
  r2 = StringCases[stringBig, 
    "<hello>" ~~ st : RegularExpression["(?:(?!WRONG).)*?"] ~~ "</hello>" :> st]]
First@AbsoluteTiming[
  r3 = StringCases[stringBig, 
    RegularExpression["(?ms)<hello>((?:(?!WRONG).)*?)</hello>"] :> "$1"]]
r1 === r2 === r3
326.007

0.00696272

0.00824213

True

As one can see, the StringExpression (~~) solution with Condition (/;) is more than 4 orders of magnitude slower than StringExpression without it. What is unexpected is that pure RegularExpression solution is slower than StringExpression without Condition. This topic was discussed before but an explanation was not found.

share|improve this answer
1  
"negative lookahead" - so, "look behind"? ;) – J. M. Mar 28 at 3:50
    
Ah, "negative" as in "should not match". Okay then. – J. M. Mar 28 at 4:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.