1

While using Regex to replace keywords with value in a template, I tested the following code.

string input = "Welcome {{friend}} Get my new {{id}} with {{anonymous}} People";
            Dictionary<string, string> mydict = new Dictionary<string, string> ();
            mydict.Add("friend", "<<My Friend>>");
            mydict.Add("id", "<<Your ID>>");
            string pattern = @"(?<=\{{2})[^}}]*(?=\}{2})";// @"\{{2}^(.*?)$\}{2}";//"^[{{\\w}}]$";
            //var m = Regex.Match(input, @"\{{(.*)\}}");
            string regex = Regex.Replace(input, pattern, delegate(Match match) {
                string v = match.ToString();
                return mydict.ContainsKey(v) ? mydict[v] : v;

            });

            Console.WriteLine(regex);

The curley braces still remain in the output which is not desired

I need <<My Friend>> instead of {{ <<My Friend>> }}. I would appreciate your suggestion.

Marks
  • 79
  • 1
  • 10
  • For some alternatives: [What's a good way of doing string templating in .NET?](https://stackoverflow.com/questions/733378/whats-a-good-way-of-doing-string-templating-in-net) – Fildor Mar 29 '18 at 13:02
  • @Fildor That's one of these "best practices" questions from 2009 that would have been closed as too broad had it been asked today. – Sergey Kalinichenko Mar 29 '18 at 13:06
  • @dasblinkenlight Yes, but nevertheless, there _are_ some alternatives in the answers that _may_ be worth consideration. – Fildor Mar 29 '18 at 13:10

2 Answers2

2

Braces remain in the original text because you are using zero-width lookahead and lookbehind constructs. This leaves the content matched by (?<=...) and (?=...) outside regex's captured value, so it does not get replaced.

To fix this problem remove lookahead and lookbehind from your regex, put a capturing group around the text of the tag, and use it to search replacement dictionary:

string pattern = @"\{{2}([^}}]*)\}{2}";
...
var v = match.Group[1].Value;
return mydict.ContainsKey(v) ? mydict[v] : v;
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    A downvote of the question and a correct solution without as much as a comment is quite obnoxious. You should at least say what you think is wrong. – Sergey Kalinichenko Mar 29 '18 at 13:45
  • Hm, From Wiktor's answer: "Note that [^}}] does not mean match any text other than }}, it just matches any char other than }, same as [^}]" - but **that** doesn't deserve a dv, does it? It still works. – Fildor Mar 29 '18 at 14:17
2

You may use a simple {{(.*?)}} regex and use the Group 1 vlaue to check for the dictionary match:

string pattern = @"{{(.*?)}}";
string regex = Regex.Replace(input, pattern, delegate(Match match) {
     string v = match.Groups[1].Value;
     return mydict.ContainsKey(v) ? mydict[v] : v;
});
// => Welcome <<My Friend>> Get my new <<Your ID>> with anonymous People

The same code with a lambda expression:

string regex = Regex.Replace(input, pattern, x =>
     mydict.ContainsKey(match.Groups[1].Value) ?
                mydict[match.Groups[1].Value] : match.Groups[1].Value;
});

See the C# demo.

Note that [^}}] does not mean match any text other than }}, it just matches any char other than }, same as [^}], so .*? is preferable in this case. Or even \w+ if you only have letters, digits and underscores in between {{ and }}.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563