I figured it would be convenient to have a function that returns a pattern, that match equally nested brackets, of the specified type. Such a pattern is easy to create, but I get some error messages, related to the condition being evaluated prematurely. The two string-replacements below returns the expected result, but the second one gives the error message StringCount::strse: String or list of strings expected at position 1 in StringCount[mid,{].

How to make BalancedBracketPattern work? My idea was that HoldPattern would prevent the conditional to be evaluated before the actual pattern is used, but that did not work.

Even better, make it only match the stuff inside the brackets!

BalancedBracketPattern[left_: "{", right_: "}"] :=
   Shortest[left ~~ mid___ ~~ right] /; 
    StringCount[mid, left] == StringCount[mid, right]];

str = "{{abc}{def}}{blah-blah}";

 w : (Shortest["{" ~~ mid___ ~~ "}"] /; 
     StringCount[mid, "{"] == StringCount[mid, "}"]) :> f[w]]

StringReplace[str, w : BalancedBracketPattern[] :> f[w]]
  • $\begingroup$ Which version of Mathematica are you using? $\endgroup$
    – Carl Woll
    May 6, 2019 at 14:44
  • $\begingroup$ @CarlWoll I am using $\endgroup$ May 6, 2019 at 14:47
  • $\begingroup$ I don't get error messages for your second example. Perhaps you have lingering definitions? Try doing ClearAll[BalancedBracketPattern] and then repeating your code. $\endgroup$
    – Carl Woll
    May 6, 2019 at 14:55
  • $\begingroup$ Ah, you are correct! Is there some way to modify the code, so that it only matches the stuff inside the brackets then? $\endgroup$ May 6, 2019 at 17:20
  • 1
    $\begingroup$ related: String matching balanced pairs of braces $\endgroup$
    – WReach
    Jun 5, 2019 at 14:24

2 Answers 2


Here's an approach to match only the stuff inside the brackets (otherwise, it is equivalent to the approach in the question):

Attributes[BalancedBracketPattern] = {HoldFirst};
  BalancedBracketPattern[name_Symbol: defName, left_: "{", right_: "}"] :=
     left ~~ (name___) ~~ right /;
      StringCount[name, left] == StringCount[name, right]

str = "{{abc}{def}}{blah-blah}{12{12}{}124}";
StringReplace[str, BalancedBracketPattern[w] :> f[w]]

A few notes:

  • We set the HoldFirst attribute to have the same behaviour as Pattern
  • We localize defName using Module to ensure that we're using a unique symbol for the default name of the pattern
  • We use Quiet to suppress the RuleDelayed::rhs warning that is generated because we're using the argument as a pattern name.
  • Restricting name to _Symbol not only makes the function more foolproof, it also allows omission of the pattern name while still specifying different brackets, as e.g. in BalancedBracketPattern["{", "}"]

I found a solution, that almost works as I want:

BalancedBracketPattern[left_: "{", right_: "}"] := Module[{f},
   f[m_String] := StringCount[m, left] - StringCount[m, right];
   (Shortest[left ~~ mid___ ~~ right /; f[mid] == 0])
str = "{{abc}{def}}{blah-blah}{12{12}{}124}";
StringReplace[str, w : BalancedBracketPattern[] :> f[w]]

This returns

f["{{abc}{def}}"] ~~ f["{blah-blah}"] ~~ f["{12{12}{}124}"]

I would like to modify the function so that the return value of the above is instead

f["{abc}{def}"] ~~ f["blah-blah"] ~~ f["12{12}{}124"]

so that the pairs of matching brackets are also (automatically) removed.

  • $\begingroup$ Why not use StringReplace[str, w : BalancedBracketPattern[] :> f[StringTake[w, {2, -2}]]] $\endgroup$
    – Carl Woll
    May 6, 2019 at 17:26
  • $\begingroup$ @CarlWoll Sure, that works, but I find it a bit ugly or inconvenient. If there is a better way to do it, I would like to know, but perhaps it is not possible $\endgroup$ May 6, 2019 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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