我可以将C#字符串值转换为转义的字符串文字吗


195

在C#中,我可以像在代码中一样将字符串值转换为字符串文字吗?我想将制表符,换行符等替换为其转义序列。

如果此代码:

Console.WriteLine(someString);

产生:

Hello
World!

我想要这段代码:

Console.WriteLine(ToLiteral(someString));

生产:

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

Answers:


180

我找到了这个:

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!"

1
刚刚从Google找到这个主题。这必须是最好的,没有必要重新发明.net可以为我们做的事情
Andy Morris 2010年

16
不错,但是请注意,对于更长的字符串,这将插入“ +”运算符,换行符和缩进。我找不到关闭它的方法。
Timwi

2
逆呢?如果您有一个包含文本转义序列的文件,包括特殊字符及其ASCII码转义了?如何产生原始版本?
卢西亚诺

1
如果您运行:void Main(){Console.WriteLine(ToLiteral(“ test \” \'\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah“));}您会注意到,这并不能解决一些逃生问题,Ronnie Overby指出\ f,其他分别是\ a和\ b
costa 2013年

4
有没有办法使它输出逐字(@"...")文字?
rookie1024 '16

38

Regex.Escape(String)呢?

Regex.Escape通过用转义码替换字符来最小化字符集(\,*,+,?,|,{,[,(,),^,$,。,#和空白)。


6
+1不知道为什么会这样。其他答案太冗长,看起来像是在重塑轮子
Adriano Carneiro 2014年

39
这不是OP所要求的。它不返回字符串文字,而是返回带有Regex特殊字符的字符串。这将Hello World?变成Hello World\?,但这是无效的字符串文字。
atheaos 2015年

1
我同意@atheaos,这是一个非常不同的问题的好答案。
hypehuman 2015年

5
即使+1并不能完全回答OP的问题,但+1是我(因此我怀疑也许是其他人)在遇到此问题时正在寻找的东西。:)
GazB

这将无法按需工作。正则表达式特殊字符不相同。例如,它将适用于\ n,但是当您有空格时,它将转换为“ \”,这不是C#会做的...
Ernesto

24

编辑:更结构化的方法,包括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('\''));

    }
}

这并不是所有的转义序列;)
TcK

1
比上面的解决方案效果更好-可以轻松添加其他转义序列。
亚诺·彼得斯

接受的答案中的逐字逐句地激怒了我。这对我来说是100%有效的。替换为正则表达式@"[\a\b\f\n\r\t\v\\""/]",并添加m_replaceDict.Add("/", @"\/");JSON
有趣的名称-这里

另外,如果需要,还必须在其中添加引号。
有趣的名字,这里

19
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)];
    }
}

1
为什么字典的第一个值中有3个反斜杠和2个语音标记?
James Yeoman

好的答案,@ JamesYeoman,因为正则表达式模式需要转义。
阿里·穆萨维·赫拉德

18

尝试:

var t = HttpUtility.JavaScriptStringEncode(s);

不起作用。如果我有“ abc \ n123”(不带引号,8个字符),我想要“ abc” + \ n +“ 123”(7个字符)。而是生成“ abc” +“ \\” +“ \ n123”(9个字符)。注意,斜杠已加倍,并且仍然包含字符串文字“ \ n”作为两个字符,而不是转义字符。
保罗

2
@Paul您想要的与问题所提出的相反。根据您的描述,这可以回答问题,因此可以正常工作。
基金莫妮卡的诉讼

我发现这对于在前端转义活动目录名称很有用
chakeda

18

完全可行的实现,包括转义Unicode和ASCII不可打印字符。不要像Hallgrim的答案那样插入“ +”号。

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

2
您应该Char.GetUnicodeCategory(c) == UnicodeCategory.Control用来决定是否转义它,否则不说ASCII的人会不会很高兴。
deerchao

这取决于您的结果字符串是否将在支持unicode的环境中使用。
Smilediver 2013年

我添加input = input ?? string.Empty;为方法的第一行,因此可以传递null并返回""而不是null引用异常。
安迪

真好 将封闭引号更改为',现在您可以通过repr(a_string):) 获得Python所提供的功能。
z33k

17

Hallgrim的答案非常好,但是“ +”,换行符和缩进添加对我来说是功能的破坏。一种简单的解决方法是:

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\"");
Bob

这新增literal = literal.Replace("/", @"\/");JSON功能。
有趣的名字,这里

这是100%直接的答案,也是唯一正确的答案!所有其他答案要么都不理解问题,要么重新发明了轮子。
bytecode77

遗憾的是,无法在DOTNET CORE下使它正常工作。有人有更好的答案吗?
sk

8

这对Smilediver的回答有一点改进,它不会转义所有非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();
    }
}

8

有趣的问题。

如果找不到更好的方法,则可以随时替换。
如果您选择它,则可以使用以下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#常见问题中找到此列表。 哪些字符转义序列可用?


2
此链接不再有效,这是为什么不鼓励仅链接的答案的教科书示例。
詹姆斯

非常正确,@ James,但是感谢Jamie Twells再次提供了信息:+1:
Nelson Reis,

5

Roslyn 在nuget上的Microsoft.CodeAnalysis.CSharp包中提供了一种方法:

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

显然,在提出原始问题时还不存在,但可能会帮助那些从Google来到这里的人。


3

如果JSON约定足以用于未转义的字符串,那么您想对其进行转义,并且已经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 + "\"");
    }
}

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

2

我试图将ToVerbatim添加到Hallgrim上面接受的答案中:

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

1

哈格里姆的答案非常好。如果您需要使用ac#正则表达式解析其他空格字符和换行符,请稍作调整。在将序列化的Json值插入到Google表格中的情况下,我需要这样做,并且在代码插入制表符,+,空格等时遇到麻烦。

  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;

-1

我提交了自己的实现,该实现可处理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();
  }
}

-7

码:

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

这是你想要的吗?


我有someString1,但是它是从文件中读取的。我希望它在调用某些方法后显示为someString2。
哈格里姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.