Kann ich einen C # -String-Wert in ein Escape-String-Literal konvertieren?

195

Kann ich in C # einen Zeichenfolgenwert in ein Zeichenfolgenliteral konvertieren, wie ich es im Code sehen würde? Ich möchte Tabulatoren, Zeilenumbrüche usw. durch ihre Escape-Sequenzen ersetzen.

Wenn dieser Code:

Console.WriteLine(someString);

produziert:

Hello
World!

Ich möchte diesen Code:

Console.WriteLine(ToLiteral(someString));

produzieren:

\tHello\r\n\tWorld!\r\n
Hallgrim
quelle

Antworten:

180

Ich habe das gefunden:

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

Dieser Code:

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

Produziert:

    Hello
    World!
"\tHello\r\n\tWorld!"
Hallgrim
quelle
1
Habe gerade das von Google gefunden. Dies muss das Beste sein, es macht keinen Sinn, Dinge neu zu erfinden, die .net für uns tun kann
Andy Morris
16
Schön, aber beachten Sie, dass bei längeren Zeichenfolgen "+" - Operatoren, Zeilenumbrüche und Einrückungen eingefügt werden. Ich konnte keinen Weg finden, das auszuschalten.
Timwi
2
Was ist mit der Umkehrung? Wenn Sie eine Datei mit Text haben, der Escape-Sequenzen enthält, einschließlich eines bestimmten Zeichens, das mit seinem ASCII-Code maskiert wurde? Wie erstelle ich eine Rohversion?
Luciano
1
Wenn Sie Folgendes ausführen: void Main () {Console.WriteLine (ToLiteral ("test \" \ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah "));} Sie werden feststellen, dass dies nicht für ein paar Fluchten sorgt. Ronnie Overby zeigte auf \ f, die anderen sind \ a und \ b
costa
4
Gibt es eine Möglichkeit, wörtliche ( @"...") Literale auszugeben ?
Rookie1024
38

Was ist mit Regex.Escape (String) ?

Regex.Escape entgeht einem minimalen Satz von Zeichen (\, *, +,?, |, {, [, (,), ^, $,., # Und Leerzeichen), indem sie durch ihre Escape-Codes ersetzt werden.

Shqdooow
quelle
6
+1 keine Ahnung warum das so weit unten ist. Andere Antworten sind einfach zu ausführlich und sehen aus, als würden sie Räder neu erfinden
Adriano Carneiro
39
Dies ist nicht das, wonach OP verlangt. Es wird kein Zeichenfolgenliteral zurückgegeben, sondern eine Zeichenfolge, bei der Regex-Sonderzeichen maskiert sind. Dies würde sich Hello World?in verwandeln Hello World\?, aber das ist ein ungültiges String-Literal.
Atheaos
1
Ich stimme @atheaos zu, dies ist eine großartige Antwort auf eine ganz andere Frage.
Hypehuman
5
+1, obwohl es die Frage des OP nicht ganz beantwortet, war es das, wonach ich (und ich vermute, vielleicht auch andere) gesucht habe, als ich auf diese Frage gestoßen bin. :)
GazB
Dies funktioniert nicht nach Bedarf. Die Regex-Sonderzeichen sind nicht identisch. Es wird zum Beispiel für \ n funktionieren, aber wenn Sie ein Leerzeichen haben, wird es in "\" konvertiert, was C # nicht tun würde ...
Ernesto
24

EDIT: Ein strukturierterer Ansatz, einschließlich aller Escape-Sequenzen für strings und chars.
Ersetzt Unicode-Zeichen nicht durch ihre wörtliche Entsprechung. Kocht auch keine Eier.

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
quelle
Dies sind nicht alle Escape-Sequenzen;)
TcKs
1
Funktioniert besser als die obige Lösung - und andere Escape-Sequenzen können einfach hinzugefügt werden.
Arno Peters
Wörtlich in der akzeptierten Antwort machte mich verrückt. Dies funktioniert zu 100% für meinen Zweck. Regex durch ersetzt @"[\a\b\f\n\r\t\v\\""/]"und hinzugefügt m_replaceDict.Add("/", @"\/");für JSON.
interessanter Name hier
Außerdem müssen Sie die beiliegenden Zitate hinzufügen, wenn Sie diese möchten.
interessanter Name hier
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
quelle
1
Warum enthält der erste Wert des Wörterbuchs drei Backslashes und zwei Sprachmarkierungen?
James Yeoman
Gute Antwort, @JamesYeoman, weil das Regex-Muster maskiert werden muss.
Ali Mousavi Kherad
18

Versuchen:

var t = HttpUtility.JavaScriptStringEncode(s);
Arsen Zahray
quelle
Funktioniert nicht. Wenn ich "abc \ n123" (ohne Anführungszeichen, 8 Zeichen) habe, möchte ich "abc" + \ n + "123" (7 Zeichen). Stattdessen wird "abc" + "\\" + "\ n123" (9 Zeichen) erzeugt. Beachten Sie, dass der Schrägstrich verdoppelt wurde und weiterhin ein Zeichenfolgenliteral von "\ n" als zwei Zeichen enthält, nicht das maskierte Zeichen.
Paul
2
@Paul Was Sie wollen, ist das Gegenteil von dem, was die Frage stellt. Dies beantwortet gemäß Ihrer Beschreibung die Frage und funktioniert daher .
Fund Monica Klage
Ich fand dies nützlich, um Active Directory-Namen im Frontend zu entkommen
Chakeda
18

Voll funktionsfähige Implementierung, einschließlich Escapezeichen für nicht druckbare Unicode- und ASCII-Zeichen. Fügt keine "+" - Zeichen wie Hallgrims Antwort ein .

    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
quelle
2
Sie sollten verwenden Char.GetUnicodeCategory(c) == UnicodeCategory.Control, um zu entscheiden, ob Sie entkommen möchten, oder Personen, die kein ASCII sprechen, werden nicht sehr glücklich sein.
Deerchao
Dies hängt von der Situation ab, ob die resultierende Zeichenfolge in der Umgebung verwendet wird, die Unicode unterstützt, oder nicht.
Smilediver
Ich habe input = input ?? string.Empty;als erste Zeile der Methode hinzugefügt , damit ich anstelle einer Nullreferenzausnahme übergeben nullund zurückkehren kann "".
Andy
Nett. Ändern Sie die Anführungszeichen in 'und jetzt haben Sie das, was Python Ihnen sofort mit repr(a_string):) gibt.
z33k
17

Die Antwort von Hallgrim ist ausgezeichnet, aber die Ergänzungen "+", Zeilenumbruch und Einzug haben die Funktionalität für mich beeinträchtigt. Ein einfacher Weg, dies zu umgehen, ist:

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
quelle
Funktioniert super. Ich habe auch eine Zeile vor dem hinzugefügt return literal, um die Lesbarkeit literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
Bob
Dies wurde literal = literal.Replace("/", @"\/");für die JSONFunktionalität hinzugefügt .
interessanter-Name-hier
Dies ist 100% einfach und die einzig richtige Antwort! Alle anderen Antworten haben die Frage entweder nicht verstanden oder das Rad neu erfunden.
Bytecode77
Traurig, kann dies unter DOTNET CORE nicht zum Laufen bringen. Hat jemand eine bessere Antwort?
Sk
8

Hier ist eine kleine Verbesserung für Smiledivers Antwort: Es wird nicht allen Nicht-ASCII-Zeichen entgehen, sondern nur diese werden wirklich benötigt.

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
quelle
8

Interessante Frage.

Wenn Sie keine bessere Methode finden, können Sie diese jederzeit ersetzen.
Falls Sie sich dafür entscheiden, können Sie diese C # Escape-Sequenzliste verwenden :

  • \ '- einfaches Anführungszeichen, das für Zeichenliterale benötigt wird
  • "- doppeltes Anführungszeichen, wird für String-Literale benötigt
  • \ - Backslash
  • \ 0 - Unicode-Zeichen 0
  • \ a - Warnung (Zeichen 7)
  • \ b - Rücktaste (Zeichen 8)
  • \ f - Formularvorschub (Zeichen 12)
  • \ n - Neue Zeile (Zeichen 10)
  • \ r - Wagenrücklauf (Zeichen 13)
  • \ t - Registerkarte "Horizontal" (Zeichen 9)
  • \ v - Vertikales Anführungszeichen (Zeichen 11)
  • \ uxxxx - Unicode-Escape-Sequenz für Zeichen mit Hex-Wert xxxx
  • \ xn [n] [n] [n] - Unicode-Escape-Sequenz für Zeichen mit Hex-Wert nnnn (Version mit variabler Länge von \ uxxxx)
  • \ Uxxxxxxxx - Unicode-Escape-Sequenz für Zeichen mit Hex-Wert xxxxxxxx (zum Generieren von Ersatzzeichen)

Diese Liste finden Sie in den häufig gestellten Fragen zu C #. Welche Zeichen-Escape-Sequenzen sind verfügbar?

Nelson Reis
quelle
2
Dieser Link funktioniert nicht mehr, ein Lehrbuchbeispiel dafür, warum von Antworten nur auf Links abgeraten wird.
James
Sehr wahr, @James, aber dank Jamie Twells sind die Informationen wieder verfügbar: +1:
Nelson Reis
5

Es gibt eine Methode dafür in Roslyns Microsoft.CodeAnalysis.CSharp- Paket auf nuget:

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

Offensichtlich gab es dies zum Zeitpunkt der ursprünglichen Frage noch nicht, aber es könnte Menschen helfen, die von Google hierher kommen.

Graham
quelle
3

Wenn JSON-Konventionen für die nicht maskierten Zeichenfolgen ausreichen, die maskiert werden sollen, und Sie sie bereits Newtonsoft.Jsonin Ihrem Projekt verwenden (es hat einen ziemlich großen Overhead), können Sie dieses Paket wie folgt verwenden:

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
quelle
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.
quelle
2

Mein Versuch, ToVerbatim zu Hallgrims oben akzeptierter Antwort hinzuzufügen :

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
quelle
1

Hallgrims Antwort war ausgezeichnet. Hier ist eine kleine Änderung für den Fall, dass Sie zusätzliche Leerzeichen und Zeilenumbrüche mit einem regulären Ausdruck ac # analysieren müssen. Ich brauchte dies im Fall eines serialisierten Json-Werts zum Einfügen in Google Sheets und bekam Probleme, da der Code Tabulatoren, +, Leerzeichen usw. einfügte.

  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
quelle
-1

Ich reiche meine eigene Implementierung ein, die nullWerte verarbeitet und aufgrund der Verwendung von Array-Nachschlagetabellen, der manuellen Hex-Konvertierung und der Vermeidung von switchAnweisungen leistungsfähiger sein sollte .

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
quelle
-7

Code:

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

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

Ausgabe:

    Hello
    World!

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

Ist das was du willst?

rfgamaral
quelle
Ich habe someString1, aber es wird aus einer Datei gelesen. Ich möchte, dass es nach dem Aufrufen einer Methode als someString2 angezeigt wird.
Hallgrim