2

Regex noob here

I have the following string:

This is a message {key1} {{key2}} {{{key3}}}, and includes {key4}.

I'm trying to extract anything between the outer curly braces. Expected matches:

key1
{key2}
{{key3}}
key4

Most SO examples cover matches on a single curly brace or a double curly brace, but not both or any variation of. Expressions like [^{\}]+(?=}) will get me the content between the inner braces, and (?<=\{).*?(?=\}) will get the leading braces except the first, but not the trailing ones.

rgvlee
  • 2,773
  • 1
  • 13
  • 20

1 Answers1

4

You may get the results you need using

var results = Regex.Matches(text, @"{({*[^{}]*}*)}")
        .Cast<Match>()
        .Select(x => x.Groups[1].Value);

See the regex demo.

Regex details

  • { - an open curly brace
  • ({*[^{}]*}*) - Group 1:
    • {* - 0 or more open curly braces
    • [^{}]* - 0 or more chars other than curly braces
    • }* - 0 or more close curly braces
  • } - a close curly brace
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    @OP A simple adjustment to ensure that the number of opening inner braces is equivalent to the number of closing braces: `Regex.Matches(text, @"{(({*)[^{}]*(}*))}").OfType().Where(m => m.Groups[2].Value.Length == m.Groups[3].Value.Length).Select(m => m.Groups[1].Value);`. – 41686d6564 stands w. Palestine Aug 17 '20 at 13:57
  • 1
    @41686d6564 In that case, we can still use a plain regex approach: `{((?>[^{}]+|(?{)|(?<-c>}))*)(?(c)(?!))}` ([demo](http://regexstorm.net/tester?p=%7b%28%28%3f%3e%5b%5e%7b%7d%5d%2b%7c%28%3f%3cc%3e%7b%29%7c%28%3f%3c-c%3e%7d%29%29*%29%28%3f%28c%29%28%3f!%29%29%7d&i=This+is+a+message+%7bkey1%7d+%7b%7bkey2%7d%7d+%7b%7b%7bkey3%7d%7d%7d%2c+and+includes+%7bkey4%7d.+%7b%7bkey5%7d)) – Wiktor Stribiżew Aug 17 '20 at 14:03
  • If anyone can come up with a regex solution, it's you :) I just wanted to provide a simple option without changing the regex pattern completely. Now, if you excuse me for a few minutes while I try to understand how the hell the pattern works :D – 41686d6564 stands w. Palestine Aug 17 '20 at 14:12
  • 1
    @41686d6564 See [this post](https://stackoverflow.com/a/7899205/3832970). – Wiktor Stribiżew Aug 17 '20 at 14:13
  • @WiktorStribiżew is it possible to further restrict the captures to where the brace count is odd, including 1? – rgvlee Aug 18 '20 at 13:12
  • @rgvlee Please clarify with examples. – Wiktor Stribiżew Aug 18 '20 at 13:23
  • E.g. for the OP string, the matches are key1, key3 and key4 as they have 1 or 3 braces each side. key2 would not be a match as the brace count on at least 1 side is even. – rgvlee Aug 18 '20 at 13:35
  • 1
    @rgvlee Then you may use `(?<!{){((?:\{{2})*[^{}]*(?:}{2})*)}(?!})` ([demo](http://regexstorm.net/tester?p=%28%3f%3c!%7b%29%7b%28%28%3f%3a%5c%7b%7b2%7d%29*%5b%5e%7b%7d%5d*%28%3f%3a%7d%7b2%7d%29*%29%7d%28%3f!%7d%29&i=This+is+a+message+%7bkey1%7d+%7b%7bkey2%7d%7d+%7b%7b%7bkey3%7d%7d%7d%2c+and+includes+%7bkey4%7d.+%7b%7bkey5%7d)) – Wiktor Stribiżew Aug 18 '20 at 14:18