Marcel Lorenz
Marcel Lorenz

Reputation: 473

C# Regex substring should be at start and end but not in the middle

Let's consider ${ the opening tag and }$ the closing tag. The opening tag should only occur at the start and the closing tag. The chars {,},$ are allowed as long as they do not form one of the tags: So ${Macro{Inner}}$ is allowed.

This is what I tried: \$\{[^((\$\{)|(\}\$))]+\}\$

Upvotes: 1

Views: 203

Answers (3)

MikeM
MikeM

Reputation: 13641

You have tried the pattern [^((\$\{)|(\}\$))]+ to prevent ${ or }$ being matched, but that is a misunderstanding of how character groups work.

[^((\$\{)|(\}\$))] means match a single character that is not a (, $, {, ), |, or }.

The following working regex is an example of how to use a negative lookahead to avoid ${ or }$ being matched:

\$\{(?:(?!\$\{|\}\$).)*\}\$

If you want to match across newlines use RegexOptions.Singleline.

(Although I have done so, it is not necessary to escape the { and } in the regex above because the regex engine can determine from the surrounding context that they should be interpreted as match the literal character.)

Upvotes: 1

Caius Jard
Caius Jard

Reputation: 74660

Don't need regex for this

s.StartsWith("${") && s.EndsWith("}$") && new[]{"${", "}$"}.All(x => x.IndexOf(x, 2, s.Length-4) == -1)

Why do I advocate not using a regex?

  • go for the simple solution, not the perfect one;
  • this code is more readable/self documenting
  • it's not so a regex so complicated that you have to ask on SO to make it work
  • you or the developer that replaces you has a more reasonable chance at maintaining it than a regex of the required complexity

Upvotes: 1

The fourth bird
The fourth bird

Reputation: 163467

If the curly's don't have to be balances, you might use

(?<!\S)\${[^{}]*(?>(?:(?<!\$){|}(?!\$))[^{}]*)*}\$(?!\S)

The pattern matches:

  • (?<!\S) Assert a whitespace boundary to the left
  • \${ match ${
  • [^{}]* Optionally repeat matching any char other than { and }
  • (?> Atomic group
    • (?: Non capture group
      • (?<!\$){ Match { asserting not { to the left
      • | Or
      • }(?!\$) Match } asserting not } to the right
    • ) Close non capture group
    • [^{}]* Optionally repeat matching any char other than { and }
  • )* Close the atomic group and optionally repeat
  • }\$ Match }$
  • (?!\S) Assert a whitespace boundary to the right

.NET regex demo


If the parenthesis should be balanced, you could use:

(?<!\S)\${(?>(?<!\$){(?<c>)|[^{}]+|}(?!\$)(?<-c>))*(?(c)(?!))}\$(?!\S)

Regex demo

Upvotes: 1

Related Questions