# Highlighting repeated matches with StringCases

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?

• I once asked something similar. – 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:

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


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

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


Example highlighting several different words:

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


• 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? – alancalvitti Aug 16 '16 at 21:24
• @alancalvitti Yes, see my update. – C. E. Aug 16 '16 at 21:26
• 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 – Jason B. Aug 16 '16 at 21:37
• @JasonB Had never seen Highlighted before, so thanks for posting :) – 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"


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