Tôi có thể chuyển đổi giá trị chuỗi C # thành chuỗi ký tự thoát không


195

Trong C #, tôi có thể chuyển đổi một giá trị chuỗi thành một chuỗi bằng chữ không, theo cách tôi sẽ thấy nó trong mã? Tôi muốn thay thế các tab, dòng mới, vv bằng các chuỗi thoát của chúng.

Nếu mã này:

Console.WriteLine(someString);

sản xuất:

Hello
World!

Tôi muốn mã này:

Console.WriteLine(ToLiteral(someString));

để sản xuất:

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

Câu trả lời:


180

Tôi đã tìm thấy cái này:

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

Mã này:

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

Sản xuất:

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

1
Chỉ cần tìm thấy điều này từ google chủ đề. Điều này phải là tốt nhất, không có điểm nào trong việc phát minh lại những thứ mà .net có thể làm cho chúng ta
Andy Morris

16
Đẹp một, nhưng lưu ý rằng đối với các chuỗi dài hơn, điều này sẽ chèn các toán tử "+", dòng mới và thụt lề. Tôi không thể tìm cách tắt nó đi.
Timwi

2
Điều gì về nghịch đảo? Nếu bạn có một tập tin với văn bản thoát khỏi trình tự thoát incluidng nhân vật đặc biệt thoát với mã ascii của nó? Làm thế nào để sản xuất một phiên bản thô?
Luciano

1
Nếu bạn chạy: void Main () {Console.WriteLine (ToLiteral ("test \" \ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah "));} bạn sẽ nhận thấy rằng điều này không quan tâm đến một vài lối thoát. Ronnie Overby đã chỉ \ f, những người khác là \ a và \ b
costa

4
Có cách nào để làm cho nó xuất ra nguyên văn ( @"...") không?
tân binh1024

38

Còn Regex.Escape (Chuỗi) thì sao?

Regex.Escape thoát khỏi một bộ ký tự tối thiểu (\, *, +,?, |, {, [, (,), ^, $,., # Và khoảng trắng) bằng cách thay thế chúng bằng mã thoát.


6
+1 không biết tại sao đây là cách dưới đây. Các câu trả lời khác chỉ là quá dài dòng và trông giống như các bánh xe phát minh lại
Adriano Carneiro

39
Đây không phải là những gì OP đang yêu cầu. Nó không trả về một chuỗi ký tự, nó trả về một chuỗi với các ký tự đặc biệt Regex đã thoát. Điều này sẽ biến Hello World?thành Hello World\?, nhưng đó là một chuỗi ký tự không hợp lệ.
atheaos

1
Tôi đồng ý với @atheaos, đây là một câu trả lời tuyệt vời cho một câu hỏi rất khác.
hypehuman

5
+1 mặc dù nó không hoàn toàn trả lời câu hỏi của OP, đó là điều mà tôi (và vì vậy tôi nghi ngờ có thể những người khác) đang tìm kiếm khi tôi gặp câu hỏi này. :)
GazB

Điều này sẽ không hoạt động khi cần thiết. Các ký tự đặc biệt regex không giống nhau. Nó sẽ hoạt động cho \ n chẳng hạn, nhưng khi bạn có một khoảng trắng, nó sẽ được chuyển đổi thành "\", đó không phải là điều C # sẽ làm ...
Ernesto

24

EDIT: Một cách tiếp cận có cấu trúc hơn, bao gồm tất cả các chuỗi thoát cho strings và chars.
Không thay thế các ký tự unicode bằng nghĩa đen của chúng. Cũng không nấu trứng.

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

    }
}

Đây không phải là tất cả các chuỗi thoát;)
TcKs

1
Hoạt động tốt hơn giải pháp trên - và các chuỗi thoát khác có thể dễ dàng được thêm vào.
Arno Peters

Nguyên văn trong câu trả lời được chấp nhận là lái xe cho tôi. Điều này hoạt động 100% cho mục đích của tôi. Thay thế regex với @"[\a\b\f\n\r\t\v\\""/]"và thêm m_replaceDict.Add("/", @"\/");cho JSON.
tên thú vị ở đây

Ngoài ra, bạn phải thêm các trích dẫn kèm theo này nếu bạn muốn.
tên thú vị ở đây

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
Tại sao có 3 dấu gạch chéo ngược và hai dấu phát biểu trong giá trị đầu tiên của từ điển?
James Yeoman

Câu trả lời hay, @JamesYeoman đó là vì mô hình regex cần phải được thoát.
Ali Mousavi Kherad

18

thử:

var t = HttpUtility.JavaScriptStringEncode(s);

Không hoạt động. Nếu tôi có "abc \ n123" (không có dấu ngoặc kép, 8 ký tự), tôi muốn "abc" + \ n + "123" (7 ký tự). Thay vào đó, nó tạo ra "abc" + "\\" + "\ n123" (9 ký tự). Lưu ý dấu gạch chéo đã được nhân đôi và nó vẫn chứa một chuỗi ký tự "\ n" là hai ký tự, không phải là ký tự thoát.
Paul

2
@Paul Những gì bạn muốn là trái ngược với những gì câu hỏi đang hỏi, mặc dù. Điều này, theo mô tả của bạn, trả lời câu hỏi, và do đó không hoạt động.
Vụ kiện của Quỹ Monica

Tôi thấy điều này hữu ích để thoát tên thư mục hoạt động trong frontend
chakena

18

Thực hiện đầy đủ hoạt động, bao gồm thoát các ký tự không thể in Unicode và ASCII. Không chèn các dấu "+" như câu trả lời của 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
Bạn nên sử dụng Char.GetUnicodeCategory(c) == UnicodeCategory.Controlđể quyết định có thoát khỏi nó hay những người không nói ASCII sẽ không vui.
Deerchao

Điều này phụ thuộc vào tình huống nếu chuỗi kết quả của bạn sẽ được sử dụng trong môi trường hỗ trợ unicode hay không.
Smilediver

Tôi đã thêm vào input = input ?? string.Empty;như là dòng đầu tiên của phương thức để tôi có thể vượt qua nullvà lấy lại ""thay vì một ngoại lệ tham chiếu null.
Andy

Đẹp. Thay đổi các trích dẫn kèm theo 'và bây giờ bạn có những gì Python cung cấp cho bạn ngoài hộp với repr(a_string):).
z33k

17

Câu trả lời của Hallgrim là tuyệt vời, nhưng "+", bổ sung dòng mới và thụt lề đã phá vỡ chức năng đối với tôi. Một cách dễ dàng xung quanh nó là:

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

Công trình tuyệt vời. Tôi cũng đã thêm một dòng trước return literalđể dễ đọc hơn: literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
Bob

Bổ sung này literal = literal.Replace("/", @"\/");cho JSONchức năng.
tên thú vị ở đây

Đây là 100% thẳng về phía trước và câu trả lời đúng duy nhất! Tất cả các câu trả lời khác đều không hiểu câu hỏi hoặc phát minh lại bánh xe.
bytecode77

Đáng buồn thay, không thể có được điều này để làm việc theo DOTNET CORE. Bất cứ ai có một câu trả lời tốt hơn?
sk

8

Đây là một cải tiến nhỏ cho câu trả lời của Smilediver, nó sẽ không thoát khỏi tất cả các ký tự không có ASCII mà chỉ những thứ này thực sự cần thiết.

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âu hỏi thú vị.

Nếu bạn không thể tìm thấy một phương pháp tốt hơn, bạn luôn có thể thay thế.
Trong trường hợp bạn đang chọn nó, bạn có thể sử dụng Danh sách trình tự thoát C # này :

  • \ '- trích dẫn duy nhất, cần thiết cho chữ nhân vật
  • \ "- trích dẫn kép, cần thiết cho chuỗi ký tự
  • \ - dấu gạch chéo ngược
  • \ 0 - Ký tự Unicode 0
  • \ a - Cảnh báo (ký tự 7)
  • \ b - Backspace (ký tự 8)
  • \ f - Nguồn cấp dữ liệu (ký tự 12)
  • \ n - Dòng mới (ký tự 10)
  • \ r - Vận chuyển trở lại (ký tự 13)
  • \ t - Tab ngang (ký tự 9)
  • \ v - Trích dẫn dọc (ký tự 11)
  • \ uxxxx - Chuỗi thoát Unicode cho ký tự có giá trị hex xxxx
  • \ xn [n] [n] [n] - Chuỗi thoát Unicode cho ký tự có giá trị hex nnnn (phiên bản có độ dài thay đổi của \ uxxxx)
  • \ Uxxxxxxxx - Chuỗi thoát Unicode cho ký tự có giá trị hex xxxxxxxx (để tạo đại diện thay thế)

Danh sách này có thể được tìm thấy trong các câu hỏi thường gặp của C # Trình tự thoát nhân vật nào có sẵn?


2
Liên kết này không còn hoạt động, một ví dụ trong sách giáo khoa về lý do tại sao các câu trả lời chỉ liên kết không được khuyến khích.
James

Rất đúng, @James, nhưng nhờ có Jamie Twells, thông tin có sẵn một lần nữa: +1:
Nelson Reis

5

Có một phương pháp cho điều này trong gói Microsoft.CodeAnalysis.CSharp của Roslyn trên nuget:

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

Rõ ràng điều này không tồn tại vào thời điểm của câu hỏi ban đầu, nhưng có thể giúp những người kết thúc ở đây từ Google.


3

Nếu các quy ước JSON là đủ cho các chuỗi không thoát mà bạn muốn thoát và bạn đã sử dụng Newtonsoft.Jsontrong dự án của mình (nó có một chi phí khá lớn), bạn có thể sử dụng gói này như sau:

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

Nỗ lực của tôi trong việc thêm ToVerbatim vào câu trả lời được chấp nhận của Hallgrim ở trên:

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

Câu trả lời của Hallgrim là tuyệt vời. Đây là một tinh chỉnh nhỏ trong trường hợp bạn cần phân tích các ký tự khoảng trắng bổ sung và ngắt dòng với biểu thức ac # thông thường. Tôi cần điều này trong trường hợp giá trị Json được tuần tự hóa để chèn vào các trang tính của google và gặp rắc rối vì mã đang chèn các tab, +, dấu cách, v.v.

  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

Tôi gửi triển khai của riêng mình, xử lý nullcác giá trị và sẽ có hiệu suất cao hơn trên tài khoản sử dụng bảng tra cứu mảng, chuyển đổi hex thủ công và tránh các switchcâu lệnh.

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

Mã số:

string someString1 = "\tHello\r\n\tWorld!\r\n";
string someString2 = @"\tHello\r\n\tWorld!\r\n";

Console.WriteLine(someString1);
Console.WriteLine(someString2);

Đầu ra:

    Hello
    World!

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

Đây có phải là những gì bạn muốn?


Tôi có someString1, nhưng nó được đọc từ một tập tin. Tôi muốn nó xuất hiện dưới dạng someString2 sau khi gọi một số phương thức.
Hallgrim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.