【问题标题】:Can I convert a C# string value to an escaped string literal?我可以将 C# 字符串值转换为转义字符串文字吗?
【发布时间】:2008-11-27 12:39:36
【问题描述】:

在 C# 中,我可以像在代码中看到的那样将字符串值转换为字符串文字吗?我想用它们的转义序列替换制表符、换行符等。

如果这个代码:

Console.WriteLine(someString);

产生:

Hello
World!

我想要这个代码:

Console.WriteLine(ToLiteral(someString));

生产:

\tHello\r\n\tWorld!\r\n

【问题讨论】:

    标签: c# string escaping


    【解决方案1】:

    我发现了这个:

    private static string ToLiteral(string input)
    {
        using (var writer = new StringWriter())
        {
            using (var provider = CodeDomProvider.CreateProvider("CSharp"))
            {
                provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
                return writer.ToString();
            }
        }
    }
    

    这段代码:

    var input = "\tHello\r\n\tWorld!";
    Console.WriteLine(input);
    Console.WriteLine(ToLiteral(input));
    

    生产:

        Hello
        World!
    "\tHello\r\n\tWorld!"
    

    【讨论】:

    • 刚从谷歌上找到这个主题。这必须是最好的,没有必要重新发明 .net 可以为我们做的事情
    • 不错,但请注意,对于较长的字符串,这将插入“+”运算符、换行符和缩进。我找不到关闭它的方法。
    • 反过来呢?如果您有一个包含转义序列的文本文件,其中包括使用其 ascii 代码转义的特殊字符?如何制作原始版本?
    • 如果你运行: void Main() { Console.WriteLine(ToLiteral("test \"\'\\\0\a\b\f\n\r\t\v\uaaaa \ \\blah")); } 你会注意到这并没有处理一些逃逸。Ronnie Overby 指向 \f,其他的是 \a 和 \b
    • 有没有办法让它逐字输出 (@"...") 文字?
    【解决方案2】:

    使用Regex.Escape(String):

    Regex.Escape 转义最小字符集(、*、+、?、|、{、[、 (,), ^, $,., #, and white space) 用转义符替换它们 代码。

    【讨论】:

    • +1 不知道为什么会这样。其他答案太冗长了,看起来像是在重新发明轮子
    • 这不是 OP 所要求的。它不返回字符串文字,它返回一个带有正则表达式特殊字符的字符串。这会将Hello World? 变成Hello World\?,但这是一个无效的字符串文字。
    • 我同意@atheaos,这是对一个非常不同的问题的一个很好的答案。
    • +1 尽管它不能完全回答 OP 的问题,但它是我(因此我怀疑其他人)在遇到这个问题时正在寻找的。 :)
    • 这不会按需要工作。正则表达式特殊字符不一样。例如,它适用于 \n,但是当你有一个空格时,它将被转换为“\”,这不是 C# 会做的......
    【解决方案3】:

    一种更结构化的方法,包括strings 和chars 的所有转义序列,是:

    它不会将 Unicode 字符替换为其文字等价物。它也不煮鸡蛋。

    public class ReplaceString
    {
        static readonly IDictionary<string, string> m_replaceDict
            = new Dictionary<string, string>();
    
        const string ms_regexEscapes = @"[\a\b\f\n\r\t\v\\""]";
    
        public static string StringLiteral(string i_string)
        {
            return Regex.Replace(i_string, ms_regexEscapes, match);
        }
    
        public static string CharLiteral(char c)
        {
            return c == '\'' ? @"'\''" : string.Format("'{0}'", c);
        }
    
        private static string match(Match m)
        {
            string match = m.ToString();
            if (m_replaceDict.ContainsKey(match))
            {
                return m_replaceDict[match];
            }
    
            throw new NotSupportedException();
        }
    
        static ReplaceString()
        {
            m_replaceDict.Add("\a", @"\a");
            m_replaceDict.Add("\b", @"\b");
            m_replaceDict.Add("\f", @"\f");
            m_replaceDict.Add("\n", @"\n");
            m_replaceDict.Add("\r", @"\r");
            m_replaceDict.Add("\t", @"\t");
            m_replaceDict.Add("\v", @"\v");
    
            m_replaceDict.Add("\\", @"\\");
            m_replaceDict.Add("\0", @"\0");
    
            //The SO parser gets fooled by the verbatim version
            //of the string to replace - @"\"""
            //so use the 'regular' version
            m_replaceDict.Add("\"", "\\\"");
        }
    
        static void Main(string[] args){
    
            string s = "here's a \"\n\tstring\" to test";
            Console.WriteLine(ReplaceString.StringLiteral(s));
            Console.WriteLine(ReplaceString.CharLiteral('c'));
            Console.WriteLine(ReplaceString.CharLiteral('\''));
    
        }
    }
    

    【讨论】:

    • 这不是所有的转义序列;)
    • 比上面的解决方案效果更好 - 并且可以轻松添加其他转义序列。
    • 接受的答案中的逐字逐句让我发疯了。这对我的目的来说 100% 有效。将正则表达式替换为@"[\a\b\f\n\r\t\v\\""/]",并为JSON 添加了m_replaceDict.Add("/", @"\/");
    • 另外,如果你想要的话,你必须在其中添加引号。
    【解决方案4】:

    这是一个完全有效的实现,包括转义 Unicode 和 ASCII 不可打印字符。它不会插入像Hallgrim's answer 这样的“+”号。

    static string ToLiteral(string input) {
        StringBuilder literal = new StringBuilder(input.Length + 2);
        literal.Append("\"");
        foreach (var c in input) {
            switch (c) {
                case '\"': literal.Append("\\\""); break;
                case '\\': literal.Append(@"\\"); break;
                case '\0': literal.Append(@"\0"); break;
                case '\a': literal.Append(@"\a"); break;
                case '\b': literal.Append(@"\b"); break;
                case '\f': literal.Append(@"\f"); break;
                case '\n': literal.Append(@"\n"); break;
                case '\r': literal.Append(@"\r"); break;
                case '\t': literal.Append(@"\t"); break;
                case '\v': literal.Append(@"\v"); break;
                default:
                    // ASCII printable character
                    if (c >= 0x20 && c <= 0x7e) {
                        literal.Append(c);
                    // As UTF16 escaped character
                    } else {
                        literal.Append(@"\u");
                        literal.Append(((int)c).ToString("x4"));
                    }
                    break;
            }
        }
        literal.Append("\"");
        return literal.ToString();
    }
    

    请注意,这也会转义所有 Unicode 字符。如果您的环境支持它们,您可以更改该部分以仅转义控制字符:

    // UTF16 control characters
    } else if (Char.GetUnicodeCategory(c) == UnicodeCategory.Control) {
        literal.Append(@"\u");
        literal.Append(((int)c).ToString("x4"));
    } else {
        literal.Append(c);
    }
    

    【讨论】:

    • 你应该使用Char.GetUnicodeCategory(c) == UnicodeCategory.Control来决定是否逃避它,否则不会说ASCII的人不会很高兴。
    • 这取决于您的结果字符串是否会在支持 unicode 的环境中使用。
    • 我在方法的第一行添加了input = input ?? string.Empty;,这样我就可以通过null 并返回"" 而不是空引用异常。
    • 不错。将封闭的引号更改为',现在您就拥有了Python 提供的开箱即用的repr(a_string) :)。
    • 你为什么逃避',因为那是不必要的?
    【解决方案5】:

    试试:

    var t = HttpUtility.JavaScriptStringEncode(s);
    

    【讨论】:

    • 不起作用。如果我有“abc\n123”(不带引号,8 个字符),我想要“abc”+\n+“123”(7 个字符)。相反,它产生“abc”+“\\”+“\n123”(9个字符)。注意斜线被加倍了,它仍然包含一个字符串文字“\n”作为两个字符,而不是转义字符。
    • @Paul 你想要的与问题所问的相反。根据您的描述,这可以回答问题,因此确实有效。
    • 我发现这对于在前端转义活动目录名称很有用
    【解决方案6】:
    public static class StringHelpers
    {
        private static Dictionary<string, string> escapeMapping = new Dictionary<string, string>()
        {
            {"\"", @"\\\"""},
            {"\\\\", @"\\"},
            {"\a", @"\a"},
            {"\b", @"\b"},
            {"\f", @"\f"},
            {"\n", @"\n"},
            {"\r", @"\r"},
            {"\t", @"\t"},
            {"\v", @"\v"},
            {"\0", @"\0"},
        };
    
        private static Regex escapeRegex = new Regex(string.Join("|", escapeMapping.Keys.ToArray()));
    
        public static string Escape(this string s)
        {
            return escapeRegex.Replace(s, EscapeMatchEval);
        }
    
        private static string EscapeMatchEval(Match m)
        {
            if (escapeMapping.ContainsKey(m.Value))
            {
                return escapeMapping[m.Value];
            }
            return escapeMapping[Regex.Escape(m.Value)];
        }
    }
    

    【讨论】:

    • 为什么字典的第一个值有3个反斜杠和2个语音标记?
    • 很好的答案,@JamesYeoman 那是因为需要转义正则表达式模式。
    【解决方案7】:

    Hallgrim's answer 非常好,但是“+”、换行符和缩进添加对我来说破坏了功能。一个简单的方法是:

    private static string ToLiteral(string input)
    {
        using (var writer = new StringWriter())
        {
            using (var provider = CodeDomProvider.CreateProvider("CSharp"))
            {
                provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, new CodeGeneratorOptions {IndentString = "\t"});
                var literal = writer.ToString();
                literal = literal.Replace(string.Format("\" +{0}\t\"", Environment.NewLine), "");
                return literal;
            }
        }
    }
    

    【讨论】:

    • 效果很好。我还在return literal 之前添加了一行以使其更具可读性:literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
    • JSON 功能添加了这个literal = literal.Replace("/", @"\/");
    • 这是 100% 直接的,也是唯一正确的答案!所有其他答案要么不理解问题,要么重新发明轮子。
    • 伤心,不能让它在 DOTNET CORE 下工作。谁有更好的答案?
    【解决方案8】:

    在 NuGet 上的 RoslynMicrosoft.CodeAnalysis.CSharp 包中有一个方法:

    private static string ToLiteral(string valueTextForCompiler)
    {
        return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(valueTextForCompiler, false);
    }
    

    显然,这在最初提出问题时并不存在,但它可能会帮助那些最终从 Google 搜索到这里的人。

    【讨论】:

    • 这是从 .net 核心实现的好方法。
    • 是的,该包支持 .NET Core 和 .NET Standard 2.0 - 这意味着它也可以从 .NET Framework 4.6.1+ 中引用
    • 在源代码生成器中也很有用。
    【解决方案9】:

    这是Smilediver's answer 的一点改进。它不会转义所有非 ASCII 字符,但只有这些是真正需要的。

    using System;
    using System.Globalization;
    using System.Text;
    
    public static class CodeHelper
    {
        public static string ToLiteral(this string input)
        {
            var literal = new StringBuilder(input.Length + 2);
            literal.Append("\"");
            foreach (var c in input)
            {
                switch (c)
                {
                    case '\'': literal.Append(@"\'"); break;
                    case '\"': literal.Append("\\\""); break;
                    case '\\': literal.Append(@"\\"); break;
                    case '\0': literal.Append(@"\0"); break;
                    case '\a': literal.Append(@"\a"); break;
                    case '\b': literal.Append(@"\b"); break;
                    case '\f': literal.Append(@"\f"); break;
                    case '\n': literal.Append(@"\n"); break;
                    case '\r': literal.Append(@"\r"); break;
                    case '\t': literal.Append(@"\t"); break;
                    case '\v': literal.Append(@"\v"); break;
                    default:
                        if (Char.GetUnicodeCategory(c) != UnicodeCategory.Control)
                        {
                            literal.Append(c);
                        }
                        else
                        {
                            literal.Append(@"\u");
                            literal.Append(((ushort)c).ToString("x4"));
                        }
                        break;
                }
            }
            literal.Append("\"");
            return literal.ToString();
        }
    }
    

    【讨论】:

      【解决方案10】:

      有趣的问题。

      如果找不到更好的方法,可以随时更换。
      如果您选择它,您可以使用这个 C# 转义序列列表

      • \' - 单引号,字符文字需要
      • \" - 双引号,字符串字面量需要
      • \ - 反斜杠
      • \0 - Unicode 字符 0
      • \a - 警报(字符 7)
      • \b - 退格(字符 8)
      • \f - 换页(字符 12)
      • \n - 换行(字符 10)
      • \r - 回车(字符 13)
      • \t - 水平制表符(字符 9)
      • \v - 竖引号(字符 11)
      • \uxxxx - 十六进制值 xxxx 字符的 Unicode 转义序列
      • \xn[n][n][n] - 十六进制值 nnnn 字符的 Unicode 转义序列(\uxxxx 的可变长度版本)
      • \Uxxxxxxxx - 十六进制值 xxxxxxxx 字符的 Unicode 转义序列(用于生成代理项)

      此列表可在 C# 常见问题中找到 What character escape sequences are available?

      【讨论】:

      • 此链接不再有效,这是一个教科书示例,说明为什么不鼓励仅链接答案。
      • 非常正确,@James,但感谢 Jamie Twells,信息再次可用:+1:
      【解决方案11】:

      如果 JSON 约定对于您想要转义的非转义字符串已经足够了,并且您已经在项目中使用了 Json.NET (Newtonsoft.Json)(它的开销非常大),您可以使用这个包,如下所示:

      using System;
      using Newtonsoft.Json;
      
      public class Program
      {
          public static void Main()
          {
              Console.WriteLine(ToLiteral(@"abc\n123"));
          }
      
          private static string ToLiteral(string input)
          {
              return JsonConvert.DeserializeObject<string>("\"" + input + "\"");
          }
      }
      

      【讨论】:

        【解决方案12】:
        public static class StringEscape
        {
          static char[] toEscape = "\0\x1\x2\x3\x4\x5\x6\a\b\t\n\v\f\r\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\"\\".ToCharArray();
          static string[] literals = @"\0,\x0001,\x0002,\x0003,\x0004,\x0005,\x0006,\a,\b,\t,\n,\v,\f,\r,\x000e,\x000f,\x0010,\x0011,\x0012,\x0013,\x0014,\x0015,\x0016,\x0017,\x0018,\x0019,\x001a,\x001b,\x001c,\x001d,\x001e,\x001f".Split(new char[] { ',' });
        
          public static string Escape(this string input)
          {
            int i = input.IndexOfAny(toEscape);
            if (i < 0) return input;
        
            var sb = new System.Text.StringBuilder(input.Length + 5);
            int j = 0;
            do
            {
              sb.Append(input, j, i - j);
              var c = input[i];
              if (c < 0x20) sb.Append(literals[c]); else sb.Append(@"\").Append(c);
            } while ((i = input.IndexOfAny(toEscape, j = ++i)) > 0);
        
            return sb.Append(input, j, input.Length - j).ToString();
          }
        }
        

        【讨论】:

        • 解释一下。例如,想法/要点是什么?例如,是否出于性能考虑?请通过编辑您的答案来回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来好像是今天写的)。
        【解决方案13】:

        我尝试将 ToVerbatim 添加到Hallgrim's accepted answer

        private static string ToLiteral(string input)
        {
            using (var writer = new StringWriter())
            {
                using (var provider = CodeDomProvider.CreateProvider("CSharp"))
                {
                    provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, new CodeGeneratorOptions { IndentString = "\t" });
                    var literal = writer.ToString();
                    literal = literal.Replace(string.Format("\" +{0}\t\"", Environment.NewLine), "");
                    return literal;
                }
            }
        }
        
        private static string ToVerbatim(string input)
        {
            string literal = ToLiteral(input);
            string verbatim = "@" + literal.Replace(@"\r\n", Environment.NewLine);
            return verbatim;
        }
        

        【讨论】:

          【解决方案14】:

          Hallgrim's answer 非常棒。这是一个小调整,以防您需要使用 C# 正则表达式解析出额外的空白字符和换行符。在将序列化的 JSON 值插入Google Sheets 的情况下,我需要这个,并且在代码插入制表符、+、空格等时遇到了麻烦。

            provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
            var literal = writer.ToString();
            var r2 = new Regex(@"\"" \+.\n[\s]+\""", RegexOptions.ECMAScript);
            literal = r2.Replace(literal, "");
            return literal;
          

          【讨论】:

            【解决方案15】:

            我提交了自己的实现,它处理null 值,并且由于使用数组查找表、手动十六进制转换和避免switch 语句,性能应该更高。

            using System;
            using System.Text;
            using System.Linq;
            
            public static class StringLiteralEncoding {
              private static readonly char[] HEX_DIGIT_LOWER = "0123456789abcdef".ToCharArray();
              private static readonly char[] LITERALENCODE_ESCAPE_CHARS;
            
              static StringLiteralEncoding() {
                // Per http://msdn.microsoft.com/en-us/library/h21280bw.aspx
                var escapes = new string[] { "\aa", "\bb", "\ff", "\nn", "\rr", "\tt", "\vv", "\"\"", "\\\\", "??", "\00" };
                LITERALENCODE_ESCAPE_CHARS = new char[escapes.Max(e => e[0]) + 1];
                foreach(var escape in escapes)
                  LITERALENCODE_ESCAPE_CHARS[escape[0]] = escape[1];
              }
            
              /// <summary>
              /// Convert the string to the equivalent C# string literal, enclosing the string in double quotes and inserting
              /// escape sequences as necessary.
              /// </summary>
              /// <param name="s">The string to be converted to a C# string literal.</param>
              /// <returns><paramref name="s"/> represented as a C# string literal.</returns>
              public static string Encode(string s) {
                if(null == s) return "null";
            
                var sb = new StringBuilder(s.Length + 2).Append('"');
                for(var rp = 0; rp < s.Length; rp++) {
                  var c = s[rp];
                  if(c < LITERALENCODE_ESCAPE_CHARS.Length && '\0' != LITERALENCODE_ESCAPE_CHARS[c])
                    sb.Append('\\').Append(LITERALENCODE_ESCAPE_CHARS[c]);
                  else if('~' >= c && c >= ' ')
                    sb.Append(c);
                  else
                    sb.Append(@"\x")
                      .Append(HEX_DIGIT_LOWER[c >> 12 & 0x0F])
                      .Append(HEX_DIGIT_LOWER[c >>  8 & 0x0F])
                      .Append(HEX_DIGIT_LOWER[c >>  4 & 0x0F])
                      .Append(HEX_DIGIT_LOWER[c       & 0x0F]);
                }
            
                return sb.Append('"').ToString();
              }
            }
            

            【讨论】:

            • 为什么switch 语句不好?它们不是由编译器优化(查找表或类似的)吗?
            【解决方案16】:

            代码:

            string someString1 = "\tHello\r\n\tWorld!\r\n";
            string someString2 = @"\tHello\r\n\tWorld!\r\n";
            
            Console.WriteLine(someString1);
            Console.WriteLine(someString2);
            

            输出:

                Hello
                World!
            
            \tHello\r\n\tWorld!\r\n
            

            【讨论】:

            • 我有一些String1,但它是从文件中读取的。我希望它在调用某个方法后显示为 someString2。
            • 字符串可能是动态创建/获取的,他需要一个方法来处理任何字符串
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-02-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多