Posso converter um valor de string C # em um literal de string com escape


195

Em C #, posso converter um valor de string em um literal de string, da maneira como o veria no código? Gostaria de substituir guias, novas linhas etc. por suas seqüências de escape.

Se este código:

Console.WriteLine(someString);

produz:

Hello
World!

Eu quero este código:

Console.WriteLine(ToLiteral(someString));

para produzir:

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

Respostas:


180

Eu achei isto:

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

Este código:

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

Produz:

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

1
Acabei de encontrar isso no google o assunto. Isso tem que ser melhor, nenhum ponto em coisas reinventando que .net pode fazer por nós
— Andy Morris

16
Bom, mas lembre-se de que, para seqüências mais longas, isso irá inserir "+" operadores, novas linhas e recuo. Não consegui encontrar uma maneira de desligar isso.
— Timwi

2
E o inverso? Se você possui um arquivo com texto contendo seqüências de escape, incluindo caracteres especiais escapados com seu código ascii? Como produzir uma versão bruta?
— Luciano

1
Se você executar: void Main () {Console.WriteLine (ToLiteral ("teste \" \ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\ blá "));} você vai perceber que isso não cuidar de alguns escapes Ronnie Overby apontou \ f, os outros são \ a e \ b.
— costa

4
Existe uma maneira de fazê-lo literalmente (literalmente @"...") de saída ?
— rookie1024

38

E o Regex.Escape (String) ?

O Regex.Escape escapa um conjunto mínimo de caracteres (\, *, +,?, |, {, [, (,), ^, $,., # E espaço em branco), substituindo-os por seus códigos de escape.


6
+1 não faz ideia do motivo disso estar abaixo. Outras respostas são muito detalhado e olhar como rodas reinventando
— Adriano Carneiro

39
Não é isso que o OP está pedindo. Ele não retorna uma string literal, retorna uma string com caracteres especiais Regex escapados. Isso iria transformar Hello World?em Hello World\?, mas que é um literal cadeia inválida.
— Atheaos

1
Concordo com @atheaos, esta é uma ótima resposta para uma pergunta muito diferente.
— Hypehuman 31/07/2015

5
+1, mesmo que não responda totalmente à pergunta do OP, era o que eu (e, portanto, suspeito que talvez outros) estivesse procurando quando me deparei com essa pergunta. :)
— GazB 8/16

Isso não funcionará conforme necessário. Os caracteres especiais regex não são os mesmos. Ele vai trabalhar para \ n por exemplo, mas quando você tem um espaço, ele será convertido para "\" que não é o C # faria ...
— Ernesto

24

EDIT: Uma abordagem mais estruturada, incluindo todas as seqüências de escape para strings e chars.
Não substitui caracteres unicode pelo seu equivalente literal. Também não cozinha ovos.

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

    }
}

Isso não é tudo seqüências de escape;)
— TcKs

1
Funciona melhor que a solução acima - e outras seqüências de escape podem ser facilmente adicionadas.
— Arno Peters

Verbatim na resposta aceita estava me deixando maluco. Isso funciona 100% para o meu propósito. Regex substituído por @"[\a\b\f\n\r\t\v\\""/]"e adicionado m_replaceDict.Add("/", @"\/");para JSON.
— nome-interessante-aqui

Além disso, você deve adicionar as citações anexas a isso, se desejar.
— nome-interessante-aqui

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
Por que há três barras invertidas e duas marcas de fala no primeiro valor do dicionário?
— James Yeoman 30/03

Boa resposta, @ JamesYeoman, porque o padrão regex precisa ser escapado.
— Ali Mousavi Kherad

18

experimentar:

var t = HttpUtility.JavaScriptStringEncode(s);

Não funciona. Se eu tiver "abc \ n123" (sem aspas, 8 caracteres), desejo "abc" + \ n + "123" (7 caracteres). Em vez disso, produz "abc" + "\\" + "\ n123" (9 caracteres). Observe que a barra foi dobrada e ainda contém uma string literal de "\ n" como dois caracteres, não o caractere de escape.
— Paulo

2
@ Paul O que você quer é o oposto do que a pergunta está fazendo, no entanto. Isso, de acordo com sua descrição, responde à pergunta e, portanto , funciona.
— Fund Monica's Lawsuit

Eu encontrei este útil para escapar nomes de diretórios ativos no frontend
— chakeda

18

Implementação totalmente funcional, incluindo escape de caracteres não imprimíveis Unicode e ASCII. Não insere sinais "+" como a resposta de 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
Você deve Char.GetUnicodeCategory(c) == UnicodeCategory.Controldecidir se quer escapar ou se as pessoas que não falam ASCII não ficarão muito felizes.
— precisa saber é

Isso depende da situação se a sequência resultante for usada no ambiente que suporta unicode ou não.
— precisa saber é o seguinte

Eu adicionei input = input ?? string.Empty;como a primeira linha do método para que eu pudesse passar nulle voltar em ""vez de uma exceção de referência nula.
— Andy Andy

Agradável. Altere as aspas anexas para 'e agora você tem o que o Python lhe fornece imediatamente repr(a_string):).
— Z33k 07/11/19

17

A resposta de Hallgrim é excelente, mas as adições de "+", nova linha e recuo estavam quebrando a funcionalidade para mim. Uma maneira fácil de contornar isso é:

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

Funciona bem. Eu também adicionei uma linha antes da return literalpara torná-lo mais legível: literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
— Bob

Adicionado isso literal = literal.Replace("/", @"\/");para JSONfuncionalidade.
— nome-interessante-aqui

Isso é 100% direto e a única resposta correta! Todas as outras respostas não entenderam a pergunta ou reinventaram a roda.
— precisa

Infelizmente, não é possível fazer isso funcionar no DOTNET CORE. Alguém tem uma resposta melhor?
— sk

8

Aqui está um pequeno aprimoramento para a resposta de Smilediver, que não escapará a todos os caracteres não-ASCII, mas somente estes são realmente necessários.

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

Pergunta interessante.

Se você não encontrar um método melhor, poderá sempre substituí-lo.
Caso esteja optando por isso, você pode usar esta lista de seqüências de escape em C # :

  • \ '- aspas simples, necessárias para literais de caracteres
  • \ "- aspas duplas, necessárias para literais de string
  • \ - barra invertida
  • \ 0 - caractere Unicode 0
  • \ a - Alerta (caractere 7)
  • \ b - Backspace (caractere 8)
  • \ f - Feed de formulário (caractere 12)
  • \ n - nova linha (caractere 10)
  • \ r - Retorno de carro (caractere 13)
  • \ t - guia Horizontal (caractere 9)
  • \ v - Citação vertical (caractere 11)
  • \ uxxxx - sequência de escape Unicode para caractere com valor hexadecimal xxxx
  • \ xn [n] [n] [n] - sequência de escape Unicode para caractere com valor hexadecimal nnnn (versão de tamanho variável de \ uxxxx)
  • \ Uxxxxxxxx - seqüência de escape Unicode para caractere com valor hexadecimal xxxxxxxx (para gerar substitutos)

Essa lista pode ser encontrada nas Perguntas freqüentes sobre C # Quais seqüências de escape de caracteres estão disponíveis?


2
Esse link não funciona mais, um exemplo de livro didático sobre por que as respostas somente para links são desencorajadas.
— James

É verdade, @ James, mas graças a Jamie Twells as informações estão disponíveis novamente: +1:
— Nelson Reis

5

Existe um método para isso no pacote Microsoft.CodeAnalysis.CSharp de Roslyn no nuget:

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

Obviamente, isso não existia no momento da pergunta original, mas pode ajudar as pessoas que acabam aqui do Google.


3

Se as convenções JSON forem suficientes para as seqüências sem escape que você deseja escapar e você já usar Newtonsoft.Jsonem seu projeto (ele tem uma sobrecarga bastante grande), você poderá usar este pacote da seguinte maneira:

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

Minha tentativa de adicionar o ToVerbatim à resposta aceita por Hallgrim acima:

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

A resposta de Hallgrim foi excelente. Aqui está um pequeno ajuste, caso você precise analisar caracteres em branco adicionais e quebras de linha com uma expressão regular ac #. Eu precisava disso no caso de um valor Json serializado para inserção nas folhas do google e tive problemas porque o código estava inserindo tabulações, +, espaços, etc.

  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

Submeto minha própria implementação, que lida com nullvalores e deve ter melhor desempenho por usar tabelas de pesquisa de matriz, conversão hexadecimal manual e switchinstruções de evitar .

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

Código:

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

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

Resultado:

    Hello
    World!

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

É isso que voce quer?


Eu tenho someString1, mas é lido de um arquivo. Eu quero que apareça como someString2 depois de chamar algum método.
— Hallgrim
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.