Bir C # dize değerini kaçan dize değişmezine dönüştürebilir miyim


196

C #, ben bir dize değeri bir dize değişmezine, kodda görmek istiyorum şekilde dönüştürebilir miyim? Sekmeleri, yeni satırları vb. Kaçış dizileriyle değiştirmek istiyorum.

Bu kod ise:

Console.WriteLine(someString);

üretir:

Hello
World!

Bu kodu istiyorum:

Console.WriteLine(ToLiteral(someString));

üretmek için:

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

Yanıtlar:


181

Bunu buldum:

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

Bu kod:

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

üretir:

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

1
Sadece konu Google'dan buldum. Bu en iyisi olmalı, .net'in bizim için yapabileceği şeyleri yeniden keşfetmenin bir anlamı yok
Andy Morris

16
Güzel olan, ancak daha uzun dizeler için bunun "+" operatörleri, yeni satırlar ve girinti ekleyeceğini unutmayın. Bunu kapatacak bir yol bulamadım.
Timwi

2
Tersi ne olacak? Eğer ascii kodu ile kaçtı especial karakteri de dahil olmak üzere kaçış dizileri içeren metin içeren bir dosya varsa? Ham sürüm nasıl üretilir?
Luciano

1
Çalıştırırsanız: void Main () {Console.WriteLine (ToLiteral ("test \" \ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah "));} bunun birkaç kaçışla ilgilenmediğini fark edeceksiniz. Ronnie Overby \ f, diğerleri \ a ve \ b
costa

4
Verbatim ( @"...") değişmezleri üretmenin bir yolu var mı ?
Ocak 16:20

39

Regex.Escape (String) ne olacak ?

Regex.Escape, kaçış kodlarıyla değiştirerek minimum karakter kümesinden (\, *, +,?, |, {, [, (,), ^, $,., # Ve beyaz boşluk) kaçar.


6
+1 bunun neden aşağıda olduğunu bilmiyorum. Diğer cevaplar çok ayrıntılı ve tekerlekleri yeniden icat etmek gibi görünüyor
Adriano Carneiro

40
OP'nin istediği bu değil. Bir dize değişmez değeri döndürmez, Regex özel karakterlerinden kaçan bir dize döndürür. Bu Hello World?dönüşür Hello World\?, ancak bu geçersiz bir dize değişmezidir.
atheaos

2
@Atheaos'a katılıyorum, bu çok farklı bir soruya harika bir cevap.
hypehuman

5
+1, OP'nin sorusuna tam olarak cevap vermese de, bu soruyla karşılaştığımda aradığım şeydi (ve belki de başkalarından şüpheliyim). :)
GazB

Bu gerektiği gibi çalışmaz. Normal ifade özel karakterleri aynı değildir. Örneğin, \ n için çalışacaktır, ancak bir yeriniz olduğunda, C \ 'nin yapacağı şey "\"
Ernesto

25

DÜZENLEME: strings ve chars için tüm kaçış dizilerini içeren daha yapılandırılmış bir yaklaşım
Unicode karakterleri değişmez eşdeğerleriyle değiştirmez. Yumurta da pişirmez.

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

    }
}

Bu tüm kaçış dizileri değil;)
TcKs

1
Yukarıdaki çözümden daha iyi çalışır - ve diğer kaçış dizileri kolayca eklenebilir.
Arno Peters

Kabul edilen cevapta aynen bana şaka yapıyordum. Bu benim amacım için% 100 çalışıyor. İle regex Değiştirilen @"[\a\b\f\n\r\t\v\\""/]"ve eklenen m_replaceDict.Add("/", @"\/");için JSON.
interesting-name-here

Ayrıca, bunları istiyorsanız, buna ekli alıntılar eklemeniz gerekir.
interesting-name-here

19

Deneyin:

var t = HttpUtility.JavaScriptStringEncode(s);

Çalışmıyor. "Abc \ n123" (tırnak işaretleri olmadan, 8 karakter) varsa, "abc" + \ n + "123" (7 karakter) istiyorum. Bunun yerine "abc" + "\\" + "\ n123" (9 karakter) üretir. Eğik çizginin iki katına çıktığını ve yine de kaçan karakteri değil, iki karakter olarak "\ n" dizgesini içerdiğine dikkat edin.
Paul

2
@Paul İstediğiniz, sorunun sorduğu soruların tersidir. Bu, sizin açıklamasına göre, soru cevaplar ve bu nedenle does işi.
Monica'nın Davası

Ben ön uç aktif dizin isimleri kaçmak için yararlı buldum
chakeda

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
Sözlüğün ilk değerinde neden 3 ters eğik çizgi ve iki konuşma işareti var?
James Yeoman

Güzel cevap, @ JamesYeoman, çünkü normal ifade modelinden kaçmak gerekiyor.
Ali Mousavi Kherad

18

Unicode ve ASCII yazdırılamayan karakterlerden kaçış da dahil olmak üzere tam çalışan uygulama. Hallgrim'in cevabı gibi "+" işaretleri koymaz .

    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
Sen kullanmalıdır Char.GetUnicodeCategory(c) == UnicodeCategory.Controlondan kaçmak için, ya ASCII bilmeyen insanlar çok mutlu olmayacak karar vermek.
deerchao

Bu, sonuçtaki dizenizin unicode destekleyen ortamda kullanılacağı veya kullanılamayacağı duruma bağlıdır.
Smilediver

input = input ?? string.Empty;Yöntemin ilk satırı olarak ekledim , böylece boş bir referans istisnası yerine geçip nullgeri dönebildim "".
Andy

Güzel. Ekteki tırnak işaretlerini değiştirin 've şimdi Python'un repr(a_string):) ile kutudan çıkardığı şeyleri elde edersiniz .
z33k

17

Hallgrim'in yanıtı mükemmel, ama "+", satırsonu ve girinti eklemeleri benim için işlevselliği kırıyordu. Etrafında kolay bir yol:

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

Harika çalışıyor. return literalDaha okunaklı hale getirmek için önce bir satır ekledim : literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
Bob

Bunu işlevsellik literal = literal.Replace("/", @"\/");için JSONekledi.
interesting-name-here

Bu% 100 düz ileri ve tek doğru cevap! Diğer tüm cevaplar soruyu anlamadı ya da tekerleği yeniden icat etti.
bytecode77

Üzücü, bunu DOTNET CORE altında çalıştıramazsınız. Daha iyi bir cevabı olan var mı?
sk

8

İşte Smilediver'ın cevabı için küçük bir iyileştirme, tüm ASCII olmayan karakterlerden kaçmayacak, sadece bunlara gerçekten ihtiyaç var.

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

İlginç soru.

Daha iyi bir yöntem bulamazsanız, her zaman değiştirebilirsiniz.
Bunu seçerseniz, bu C # Escape Sequence List'i kullanabilirsiniz :

  • \ '- tek harf, karakter değişmez değerleri için gerekli
  • \ "- dize değişmez değerleri için çift tırnak işareti gerekli
  • \ - ters eğik çizgi
  • \ 0 - Unicode karakter 0
  • \ a - Uyarı (karakter 7)
  • \ b - Geri sil (karakter 8)
  • \ f - Form besleme (karakter 12)
  • \ n - Yeni satır (karakter 10)
  • \ r - Satır başı (karakter 13)
  • \ t - Yatay sekme (karakter 9)
  • \ v - Dikey alıntı (karakter 11)
  • \ uxxxx - Onaltılık xxxx değerine sahip karakter için Unicode kaçış dizisi
  • \ xn [n] [n] [n] - Onaltılık nnnn değerine sahip karakter için Unicode kaçış dizisi (\ uxxxx değişken uzunluklu sürümü)
  • \ Uxxxxxxxx - Onaltılık değeri xxxxxxxx olan karakter için Unicode kaçış dizisi (vekil oluşturmak için)

Bu liste C # 'da bulunabilir Sık Sorulan Sorular Hangi karakter kaçış dizileri mevcuttur?


2
Bu bağlantı artık çalışmıyor, yalnızca bağlantı yanıtlarının neden cesaret kırıldığına dair bir ders kitabı örneği.
James

Çok doğru, @ James, ancak Jamie Twells sayesinde bilgiler tekrar mevcut: +1:
Nelson Reis

5

Bunun için Roslyn'in Microsoft.CodeAnalysis.CSharp paketinde nuget üzerinde bir yöntem var:

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

Açıkçası bu, orijinal soru sırasında mevcut değildi, ancak burada Google'dan gelen insanlara yardımcı olabilir.


3

Kaçmak istediğiniz çıkışsız dizeler için JSON kuralları yeterliyse ve Newtonsoft.Jsonprojenizde zaten kullanıyorsanız (oldukça büyük bir ek yüke sahiptir) bu paketi aşağıdaki gibi kullanabilirsiniz:

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

Hallgrim'in yukarıdaki kabul edilen cevabına ToVerbatim'i ekleme girişimim :

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

Hallgrim'in cevabı mükemmeldi. Ac # düzenli ifadesi ile ek boşluk karakterlerini ve satır satırlarını ayrıştırmanız gerektiğinde küçük bir değişiklik. Google sayfalarına ekleme için serileştirilmiş bir Json değeri durumunda bu gerekli ve kod sekmeler, +, boşluklar, vb. Eklerken sorunla karşılaştım.

  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

nullDeğerleri işleyen ve dizi arama tablolarını kullanma, el ile onaltılık dönüştürme ve switchifadelerden kaçınma nedeniyle daha performanslı olması gereken kendi uygulamamı gönderiyorum .

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

Kod:

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

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

Çıktı:

    Hello
    World!

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

İstediğiniz bu mu?


SomeString1 var, ama bir dosyadan okunur. Bazı yöntemi çağırdıktan sonra someString2 olarak görünmesini istiyorum.
Hallgrim
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.