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.