简体   繁体   English

插值字符串的转义引号

[英]Escape quotes for interpolated string

I have a program that generates C# from bits of C# stored in an XML file. 我有一个程序可以从存储在XML文件中的C#的位生成C#。 If I have a snippet like: 如果我有以下代码段:

foo {bar}

I need to transform that into an interpolated string, like this: 我需要将其转换为插值字符串,如下所示:

$@"foo {bar}"

The problem is that, if I have quotes outside a placeholder, eg: 问题是,如果我在占位符外部有引号,例如:

"foo" {bar}

I need to double those: 我需要将它们加倍:

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

but ignore quotes inside placeholders: 但忽略占位符内的引号:

foo {"bar"}

should produce: 应该产生:

$@"foo {"bar"}"

Also, need to look out for doubled braces: 另外,还需要注意括号是否加倍:

foo {{"bar"}}

should produce: 应该产生:

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

And perhaps the trickiest of all, if the placeholder is preceded and/or followed by an even number of braces: 如果占位符在大括号之前和/或之后,则可能是最棘手的:

foo {{{"bar"}}}

should produce: 应该产生:

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

In short, if there's a placeholder then ignore everything inside. 简而言之,如果有占位符,则忽略其中的所有内容。 For the rest of the text, double quotes. 对于其余的文本,请用双引号引起来。

Can this be accomplished using regular expressions? 可以使用正则表达式来实现吗? If not, what alternatives do I have? 如果没有,我有什么选择?

You will need at least 2 steps: 您至少需要2个步骤:

  1. Add quotes inside the expression: 在表达式内添加引号:

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

See demo 观看演示

  1. Enclose in $@"..." with string.Format("$@\\"{0}\\"", str); string.Format("$@\\"{0}\\"", str);括在$@"..." string.Format("$@\\"{0}\\"", str);

Here is an IDEONE demo 这是一个IDEONE演示

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

And another demo here 还有另一个演示

This cannot be done with regular expressions. 使用正则表达式无法做到这一点。 Knowing when a placeholder starts is easy, knowing when it ends is the hard part, since a placeholder can hold almost any C# expression, so you have to keep track of blocks ( {} ) and literals (strings, chars, comments) because any brace in a literal is not significant. 知道占位符何时开始很容易,知道占位符何时结束是困难的部分,因为占位符几乎可以容纳任何C#表达式,因此您必须跟踪块( {} )和文字(字符串,字符,注释),因为任何在文字中大括号并不重要。

This is the code I came up with: 这是我想出的代码:

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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM