内插字符串

时间:2015-07-25 19:42:29

标签: c# regex

我有一个程序可以从存储在XML文件中的C#位生成C#。如果我有一个代码片段:

foo {bar}

我需要将其转换为插值字符串,如下所示:

$@"foo {bar}"

问题在于,如果我在占位符之外有引号,例如:

"foo" {bar}

我需要加倍:

$@"""foo"" {bar}"

但忽略占位符内的引号:

foo {"bar"}

应该产生:

$@"foo {"bar"}"

另外,需要注意双重括号:

foo {{"bar"}}

应该产生:

$@"foo {{""bar""}}"

也许最棘手的是,如果占位符前面和/或后面跟着偶数个括号:

foo {{{"bar"}}}

应该产生:

$@"foo {{{"bar"}}}"

简而言之,如果有占位符则忽略其中的所有内容。对于文本的其余部分,请加双引号。

这可以使用正则表达式完成吗?如果没有,我有什么替代方案?

2 个答案:

答案 0 :(得分:1)

您至少需要两个步骤:

  1. 在表达式中添加引号:

    "(?=[^}]*(?:}})*[^}]*$)|(?<=^[^{]*(?:{{)*)" =&gt; 替换为""

  2. 请参阅demo

    1. 使用$@"..."
    2. 加入string.Format("$@\"{0}\"", str);

      这是IDEONE demo

      var s = "\"foo\" {bar}";
      var rx = new Regex(@"(?<!(?<!{){[^{}]*)""(?![^{}]*}(?!}))");
      Console.WriteLine(string.Format("$@\"{0}\"",rx.Replace(s,"\"\"")));
      

      another demo here

答案 1 :(得分:1)

使用正则表达式无法做到这一点。知道占位符何时启动很容易,知道它何时结束是困难的部分,因为占位符几乎可以容纳任何C#表达式,所以你必须跟踪块({})和文字(字符串,字符,注释) )因为文字中的任何括号都不重要。

这是我提出的代码:

enum ParsingMode {
   Text,
   Code,
   InterpolatedString,
   InterpolatedVerbatimString,
   String,
   VerbatimString,
   Char,
   MultilineComment
}

public static string EscapeValueTemplate(string valueTemplate) {

   if (valueTemplate == null) throw new ArgumentNullException(nameof(valueTemplate));

   var quoteIndexes = new List<int>();

   var modeStack = new Stack<ParsingMode>();
   modeStack.Push(ParsingMode.Text);

   Func<ParsingMode> currentMode = () => modeStack.Peek();

   for (int i = 0; i < valueTemplate.Length; i++) {

      char c = valueTemplate[i];
      Func<char?> nextChar = () =>
         i + 1 < valueTemplate.Length ? valueTemplate[i + 1]
         : default(char?);

      switch (currentMode()) {
         case ParsingMode.Code:
            switch (c) {
               case '{':
                  modeStack.Push(ParsingMode.Code);
                  break;

               case '}':
                  modeStack.Pop();
                  break;

               case '\'':
                  modeStack.Push(ParsingMode.Char);
                  break;

               case '"':
                  ParsingMode stringMode = ParsingMode.String;

                  switch (valueTemplate[i - 1]) {
                     case '@':
                        if (i - 2 >= 0 && valueTemplate[i - 2] == '$') {
                           stringMode = ParsingMode.InterpolatedVerbatimString;
                        } else {
                           stringMode = ParsingMode.VerbatimString;
                        }
                        break;

                     case '$':
                        stringMode = ParsingMode.InterpolatedString;
                        break;
                  }

                  modeStack.Push(stringMode);
                  break;

               case '/':
                  if (nextChar() == '*') {
                     modeStack.Push(ParsingMode.MultilineComment);
                     i++;
                  }
                  break;
            }
            break;

         case ParsingMode.Text:
         case ParsingMode.InterpolatedString:
         case ParsingMode.InterpolatedVerbatimString:
            switch (c) {
               case '{':
                  if (nextChar() == '{') {
                     i++;
                  } else {
                     modeStack.Push(ParsingMode.Code);
                  }
                  break;

               case '"':
                  switch (currentMode()) {
                     case ParsingMode.Text:
                        quoteIndexes.Add(i);
                        break;

                     case ParsingMode.InterpolatedString:
                        modeStack.Pop();
                        break;

                     case ParsingMode.InterpolatedVerbatimString:
                        if (nextChar() == '"') {
                           i++;
                        } else {
                           modeStack.Pop();
                        }
                        break;
                  }
                  break;

               case '\\':
                  if (currentMode() == ParsingMode.InterpolatedString) {
                     i++;
                  }
                  break;
            }
            break;

         case ParsingMode.String:
            switch (c) {
               case '\\':
                  i++;
                  break;

               case '"':
                  modeStack.Pop();
                  break;
            }
            break;

         case ParsingMode.VerbatimString:
            if (c == '"') {
               if (nextChar() == '"') {
                  i++;
               } else {
                  modeStack.Pop();
               }
            }
            break;

         case ParsingMode.Char:
            switch (c) {
               case '\\':
                  i++;
                  break;
               case '\'':
                  modeStack.Pop();
                  break;
            }
            break;

         case ParsingMode.MultilineComment:
            if (c == '*') {
               if (nextChar() == '/') {
                  modeStack.Pop();
                  i++;
               }
            }
            break;
      }
   }

   var sb = new StringBuilder(valueTemplate, valueTemplate.Length + quoteIndexes.Count);

   for (int i = 0; i < quoteIndexes.Count; i++) {
      sb.Insert(quoteIndexes[i] + i, '"');
   }

   return sb.ToString();
}