Dapatkah saya mengonversi nilai string C # ke string literal yang keluar

195

Dalam C #, dapatkah saya mengonversi nilai string ke string literal, seperti cara saya melihatnya dalam kode? Saya ingin mengganti tab, baris baru, dll. Dengan urutan pelarian mereka.

Jika kode ini:

Console.WriteLine(someString);

menghasilkan:

Hello
World!

Saya ingin kode ini:

Console.WriteLine(ToLiteral(someString));

untuk menghasilkan:

\tHello\r\n\tWorld!\r\n
Hallgrim
sumber

Jawaban:

180

Aku menemukan ini:

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

Kode ini:

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

Menghasilkan:

    Hello
    World!
"\tHello\r\n\tWorld!"
Hallgrim
sumber
1
Baru saja menemukan ini dari google subjek. Ini harus menjadi yang terbaik, tidak ada gunanya menciptakan kembali hal-hal yang .net bisa lakukan untuk kita
Andy Morris
16
Bagus, tetapi perlu diketahui bahwa untuk string yang lebih panjang, ini akan memasukkan "+" operator, baris baru dan lekukan. Saya tidak dapat menemukan cara untuk mematikannya.
Timwi
2
Bagaimana dengan kebalikannya? Jika Anda memiliki file dengan teks yang terdiri dari sekuens escape termasuk karakter utama lolos dengan kode ascii-nya? Bagaimana cara menghasilkan versi mentah?
Luciano
1
Jika Anda menjalankan: void Main () {Console.WriteLine (ToLiteral ("test \" \ '\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\ blah "));} Anda akan melihat bahwa ini tidak menangani beberapa pelarian. Ronnie Overby menunjuk \ f, yang lain \ a dan \ b
costa
4
Apakah ada cara untuk membuatnya menghasilkan kata demi kata ( @"...") literal?
rookie1024
38

Bagaimana dengan Regex.Escape (String) ?

Regex.Escape lolos dari serangkaian karakter minimal (\, *, +,?, |, {, [, (,), ^, $,., #, Dan spasi putih) dengan menggantinya dengan kode pelarian mereka.

Shqdooow
sumber
6
+1 tidak tahu mengapa ini jauh di bawah. Jawaban lain terlalu bertele-tele dan terlihat seperti menciptakan kembali roda
Adriano Carneiro
39
Ini bukan yang diminta OP. Itu tidak mengembalikan string literal, itu mengembalikan string dengan karakter khusus Regex lolos. Ini akan berubah Hello World?menjadi Hello World\?, tetapi itu adalah string literal yang tidak valid.
atheaos
1
Saya setuju dengan @atheaos, ini adalah jawaban yang bagus untuk pertanyaan yang sangat berbeda.
hypehuman
5
+1 meskipun tidak cukup menjawab pertanyaan OP itu yang saya (dan jadi saya curiga mungkin orang lain) cari ketika saya menemukan pertanyaan ini. :)
GazB
Ini tidak akan berfungsi sesuai kebutuhan. Karakter khusus regex tidak sama. Sebagai contoh, ini akan berfungsi untuk \ n, tetapi ketika Anda memiliki spasi, itu akan dikonversi menjadi "\" yang bukan apa yang akan dilakukan C # ...
Ernesto
24

EDIT: Pendekatan yang lebih terstruktur, termasuk semua urutan pelarian untuk strings dan chars.
Tidak mengganti karakter unicode dengan padanan literalnya. Juga tidak memasak telur.

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

    }
}
Cristian Diaconescu
sumber
Ini tidak semua urutan melarikan diri;)
TcK
1
Bekerja lebih baik daripada solusi di atas - dan urutan pelarian lainnya dapat dengan mudah ditambahkan.
Arno Peters
Verbatim dalam jawaban yang diterima membuatku gila. Ini berfungsi 100% untuk tujuan saya. Ganti regex dengan @"[\a\b\f\n\r\t\v\\""/]"dan ditambahkan m_replaceDict.Add("/", @"\/");untuk JSON.
nama-menarik-di sini
Juga, Anda harus menambahkan kutipan terlampir untuk ini jika Anda menginginkannya.
nama-menarik-di sini
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)];
    }
}
ICR
sumber
1
Mengapa ada 3 garis miring terbalik dan dua tanda bicara pada nilai pertama kamus?
James Yeoman
Jawaban yang bagus, @JamesYeoman itu karena pola regex perlu diloloskan.
Ali Mousavi Kherad
18

mencoba:

var t = HttpUtility.JavaScriptStringEncode(s);
Arsen Zahray
sumber
Tidak bekerja. Jika saya memiliki "abc \ n123" (tanpa tanda kutip, 8 karakter), saya ingin "abc" + \ n + "123" (7 karakter). Alih-alih itu menghasilkan "abc" + "\\" + "\ n123" (9 karakter). Perhatikan bahwa garis miring digandakan dan masih berisi string literal "\ n" sebagai dua karakter, bukan karakter yang lolos.
Paul
2
@ Paul Apa yang Anda inginkan adalah kebalikan dari apa yang ditanyakan. Ini, menurut uraian Anda, menjawab pertanyaan, dan karena itu berhasil.
Dana Gugatan Monica
Saya menemukan ini berguna untuk menghindari nama direktori aktif di frontend
chakeda
18

Implementasi yang berfungsi penuh, termasuk melarikan diri dari Unicode dan karakter ASCII yang tidak dapat dicetak. Tidak menyisipkan tanda "+" seperti jawaban 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();
    }
Smilediver
sumber
2
Anda harus menggunakan Char.GetUnicodeCategory(c) == UnicodeCategory.Controluntuk memutuskan apakah akan menghindarinya, atau orang yang tidak berbicara ASCII tidak akan sangat senang.
deerchao
Ini tergantung pada situasi jika string yang Anda hasilkan akan digunakan di lingkungan yang mendukung unicode atau tidak.
Smilediver
Saya menambahkan input = input ?? string.Empty;sebagai baris pertama dari metode ini sehingga saya bisa lulus nulldan mendapatkan kembali, ""bukan pengecualian referensi nol.
Andy
Bagus. Ubah tanda kutip terlampir 'dan sekarang Anda memiliki apa yang Python berikan kepada Anda di luar kotak dengan repr(a_string):).
z33k
17

Jawaban Hallgrim sangat bagus, tetapi penambahan "+", baris baru, dan lekukan merusak fungsi bagi saya. Cara mudah untuk mengatasinya adalah:

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;
        }
    }
}
lesur
sumber
Bagus sekali. Saya juga menambahkan satu baris sebelum return literalmembuatnya lebih mudah dibaca: literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
Bob
Menambahkan ini literal = literal.Replace("/", @"\/");untuk JSONfungsionalitas.
nama-menarik-di sini
Ini 100% lurus ke depan dan satu-satunya jawaban yang benar! Semua jawaban lain entah tidak mengerti pertanyaannya atau menciptakan kembali roda.
bytecode77
Sedih, tidak dapat mengaktifkan ini di bawah DOTNET CORE. Adakah yang memiliki jawaban yang lebih baik?
sk
8

Inilah sedikit perbaikan untuk jawaban Smilediver, itu tidak akan luput dari semua karakter tanpa ASCII tetapi hanya ini yang benar-benar dibutuhkan.

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

Pertanyaan menarik.

Jika Anda tidak dapat menemukan metode yang lebih baik, Anda selalu dapat mengganti.
Jika Anda memilih untuk itu, Anda bisa menggunakan C # Escape Sequence List ini :

  • \ '- kutipan tunggal, diperlukan untuk literal karakter
  • \ "- kutipan ganda, diperlukan untuk string literal
  • \ - garis miring terbalik
  • \ 0 - Karakter Unicode 0
  • \ a - Lansiran (karakter 7)
  • \ b - Spasi mundur (karakter 8)
  • \ f - Umpan formulir (karakter 12)
  • \ n - Baris baru (karakter 10)
  • Kembali - gerbong (karakter 13)
  • \ t - Tab Horizontal (karakter 9)
  • \ v - Kutipan vertikal (karakter 11)
  • \ uxxxx - Urutan keluar Unicode untuk karakter dengan nilai hex xxxx
  • \ xn [n] [n] [n] - Urutan escape Unicode untuk karakter dengan nilai hex nnnn (versi panjang variabel \ uxxxx)
  • \ Uxxxxxxxx - Urutan keluar Unicode untuk karakter dengan nilai hex xxxxxxxx (untuk menghasilkan pengganti)

Daftar ini dapat ditemukan di C # Pertanyaan Yang Sering Diajukan Apa urutan pelarian karakter yang tersedia?

Nelson Reis
sumber
2
Tautan ini tidak lagi berfungsi, sebuah contoh buku teks tentang mengapa jawaban tautan saja tidak disarankan.
James
Sangat benar, @ James, tetapi terima kasih kepada Jamie Twells, informasinya tersedia lagi: +1:
Nelson Reis
5

Ada metode untuk ini dalam paket Microsoft.CodeAnalysis.CSharp Roslyn di nuget:

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

Jelas ini tidak ada pada saat pertanyaan awal, tetapi dapat membantu orang-orang yang berakhir di sini dari Google.

Graham
sumber
3

Jika konvensi JSON cukup untuk string yang tidak terhindar yang ingin Anda lepas dan Anda sudah menggunakan Newtonsoft.Jsondalam proyek Anda (memiliki overhead yang cukup besar), Anda dapat menggunakan paket ini seperti berikut:

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 + "\"");
    }
}
Ehsan88
sumber
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();
  }
}
Serge N
sumber
2

Upaya saya menambahkan ToVerbatim ke jawaban Hallgrim yang diterima di atas:

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;
}
Derek
sumber
1

Jawaban Hallgrim sangat bagus. Berikut ini adalah perubahan kecil jika Anda perlu mengurai karakter spasi tambahan dan linebreak dengan ekspresi reguler ac #. Saya membutuhkan ini dalam kasus nilai Json serial untuk dimasukkan ke dalam lembar google dan mengalami masalah ketika kode itu memasukkan tab, +, spasi, dll.

  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;
Alexander Yoshi
sumber
-1

Saya mengirimkan implementasi saya sendiri, yang menangani nullnilai-nilai dan harus lebih berprestasi karena menggunakan tabel pencarian array, konversi hex manual, dan menghindari switchpernyataan.

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

Kode:

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

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

Keluaran:

    Hello
    World!

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

Apa ini yang kau inginkan?

rfgamaral
sumber
Saya memiliki someString1, tetapi dibaca dari file. Saya ingin ini muncul sebagai someString2 setelah memanggil beberapa metode.
Hallgrim