插入字符串的转义引号

本文关键字:转义 字符串 插入 | 更新日期: 2023-09-27 18:04:53

我有一个程序,它从存储在XML文件中的c#位生成c#。如果我有这样的代码片段:

foo {bar}

我需要把它转换成一个插值字符串,像这样:

$@"foo {bar}"
问题是,如果我在占位符外加上引号,例如:
"foo" {bar}

我需要把它们翻倍:

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

但忽略占位符内的引号:

foo {"bar"}

应该产生:

$@"foo {"bar"}"

还有,需要注意双括号:

foo {{"bar"}}

应该产生:

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

可能是最棘手的,如果占位符前面和/或后面有偶数个大括号:

foo {{{"bar"}}}

应该产生:

$@"foo {{{"bar"}}}"
简而言之,如果有占位符,则忽略其中的所有内容。其余部分用双引号

可以使用正则表达式完成吗?如果不是,我有什么选择?

插入字符串的转义引号

你至少需要两个步骤:

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

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

看到演示

  • $@"..."string.Format("$@'"{0}'"", str);围在一起
  • 这是一个IDEONE演示

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

    这里还有一个演示

    正则表达式不能这样做。知道占位符何时开始是容易的,知道它何时结束是困难的部分,因为占位符几乎可以容纳任何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();
    }