This is a two-part question:

First, given

txt = "the quick brown fox jumps over the lazy dog"

Why does this repeated pattern match both instances:

txt // StringCases[(t : "the") .. :> t]

{"the", "the"}

Yet this matches only the 2nd instance?

txt // StringCases[(pre___ ~~ t : "the" ~~ post___) .. :> 
   Row[{pre, Style[t, Red], post}]]

{"the quick brown fox jumps over (the) lazy dog"}

(* note output above uses: pre <> "(" <> t <> ")" <> post  *)

Second, provided the first part is solved, what's the best way to output a single string with all instances highlighted, rather than a list of matches?

  • 2
    $\begingroup$ I once asked something similar. $\endgroup$ – wxffles Aug 16 '16 at 21:22

String patterns will always match the longest string possible, since pre___ and post___ match anything the second pattern will swallow the whole string. It will not look for further matches inside already matched parts of the string. This is related to the Overlaps option, but there is no way to get what we want with Overlaps and this pattern.

With Shortest we can modify this behavior, perhaps not to achieve what we want though:

 Shortest[pre___ ~~ t : "the" ~~ post___ ..] :> Row[{pre, Style[t, Red], post}]

Mathematica graphics

The best way to achieve highlighting is using StringReplace with StyleBox:

 "the" -> "\*StyleBox[\"the\", FontColor->RGBColor[1, 0, 0]]"

Mathematica graphics

Example highlighting several different words:

 (t : "the" | "jumps") :> "\*StyleBox[\"" <> t <> "\", FontColor->RGBColor[1, 0, 0]]"

Mathematica graphics

  • $\begingroup$ Although I didn't mention it in the Q, I need to be able to match Alternatives, eg ("the" | "jumps") - hence was gravitating toward StringCases. Can you make your without writing a helper function? $\endgroup$ – alancalvitti Aug 16 '16 at 21:24
  • $\begingroup$ @alancalvitti Yes, see my update. $\endgroup$ – C. E. Aug 16 '16 at 21:26
  • 1
    $\begingroup$ I had this as a separate answer but it's just like yours: Row@Apply[List]@ StringReplace[x : "the" | "jumps" :> Highlighted[x]]@txt, producing this $\endgroup$ – Jason B. Aug 16 '16 at 21:37
  • $\begingroup$ @JasonB Had never seen Highlighted before, so thanks for posting :) $\endgroup$ – C. E. Aug 16 '16 at 21:40

This is just like C.E.'s answer, using StringReplace, but with a different highlighting method

highlightText[words_List] := ReplaceAll[
   StringReplace[x : (Alternatives @@ words) :> Highlighted[x]]@#,
   StringExpression[a__] :> Row[{a}]] &

This will work on strings with and without the keywords but, unlike C.E.'s answer, when the keywords are present your result is not a String, but a Row. For display in a notebook, the difference is minimal, but it is worth taking note of.

highlightText[{"the", "jumps"}]@"the quick brown fox jumps over the lazy dog"
highlightText[{"the", "jumps"}]@"That lazy dog went to sleep"

enter image description here

  • $\begingroup$ Jason, very nice, ready to accept. However, if there's no match, then the output is Row[string]. Can you fix? $\endgroup$ – alancalvitti Sep 17 '16 at 23:24
  • $\begingroup$ @alancalvitti Ja, but it might be until tomorrow before I can open MMA $\endgroup$ – Jason B. Sep 18 '16 at 0:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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