Чи можу я перетворити значення рядка C # у ліній, що увійшов у рядок


196

Чи можу я перетворити значення рядка в літеральний рядок на C #, як я його бачив у коді? Я хотів би замінити вкладки, нові рядки тощо своїми послідовностями.

Якщо цей код:

Console.WriteLine(someString);

виробляє:

Hello
World!

Я хочу цей код:

Console.WriteLine(ToLiteral(someString));

виробляти:

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

Відповіді:


181

Я знайшов це:

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

Цей код:

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

Виробляє:

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

1
Щойно знайшов це з теми Google. Це повинно бути найкращим, не має сенсу винаходити речі, які .net може зробити для нас
Енді Морріс,

16
Хороший, але майте на увазі, що для довших рядків це буде вставляти "+" оператори, нові рядки та відступи. Я не зміг знайти спосіб відключити це.
Тімві

2
А як із зворотним? Якщо у вас є файл з текстом, що містить контактні послідовності, включаючи спеціальний символ, уникнути разом із кодом ascii? Як виготовити необроблену версію?
Лучано

1
Якщо запустити: void Main () {Console.WriteLine (ToLiteral ("тест \" \ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah));} ви помітите, що це не піклується про кілька втеч. Ронні Овербі вказав \ f, інші - \ a і \ b
Коста

4
Чи є спосіб зробити так, щоб вивести дословно (@"..." ) літерали?
rookie1024

39

Що з Regex.Escape (String) ?

Regex.Escape уникає мінімального набору символів (\, *, +,?, |, {, [, (,), ^, $,., # І пробіл), замінюючи їх кодами евакуації.


6
+1 не знаю, чому це так нижче. Інші відповіді просто занадто багатослівні і виглядають як винаходити колеса
Адріано Карнейро

40
Це не те, що просять ОП. Він не повертає рядковий літерал, він повертає рядок з уникнутими спеціальними символами Regex. Це перетвориться Hello World?на Hello World\?, але це недійсний літеральний рядок.
атеаос

2
Я погоджуюся з @atheaos, це чудова відповідь на зовсім інше питання.
гіпегуман

5
+1, незважаючи на те, що це не зовсім відповідає на питання ОП, це було те, що я шукав (і тому я підозрюю, що, можливо, інші) шукав, коли натрапив на це питання. :)
ГазБ

Це не спрацює за необхідності. Спеціальні символи регулярного вираження не однакові. Наприклад, він буде працювати \ n, але коли у вас є пробіл, він буде перетворений на "\", що не те, що C # би робив ...
Ернесто,

25

EDIT: Більш структурований підхід, що включає всі послідовності виходу для strings та chars.
Не замінює символи unicode з їх буквальним еквівалентом. Яйця теж не готують.

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

    }
}

Це ще не всі послідовності втечі;)
TcKs

1
Працює краще, ніж рішення вище - і інші послідовності втечі можна легко додати.
Арно Петерс

Дослівний у прийнятій відповіді керував мені бондерками. Це працює на 100% для моєї мети. Замінено регулярний вираз @"[\a\b\f\n\r\t\v\\""/]"і додано m_replaceDict.Add("/", @"\/");для JSON.
цікаво-ім’я-ось

Крім того, ви повинні додати додаючі цитати до цього, якщо ви хочете їх.
цікаво-ім’я-ось

19

спробуйте:

var t = HttpUtility.JavaScriptStringEncode(s);

Не працює. Якщо у мене є "abc \ n123" (без лапок, 8 символів), я хочу "abc" + \ n + "123" (7 символів). Натомість він створює "abc" + "\\" + "\ n123" (9 знаків). Зауважте, коса коса риса подвоєна, і вона все ще містить літеру з рядком "\ n" як два символи, а не символ, що уникнув.
Павло

2
@Paul Однак те, що ви хочете, - це протилежне тому, що задається питанням. Це, згідно з вашим описом, відповідає на питання, а отже , працює.
Фонд позову Моніки

Я вважаю це корисним для того, щоб уникнути імен активних каталогів у
фронтені

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
Чому в першому значенні словника є 3 зворотні косої риски та два мовні позначки?
Джеймс Єоман

Гарна відповідь, @JamesYeoman тому, що потрібно уникати шаблону регулярних виразів.
Алі Мусаві Херад

18

Повністю працююча реалізація, включаючи вихід Unicode та ASCII символів, що не друкуються. Не вставляє знаки "+", як відповідь 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
Вам слід використовувати, Char.GetUnicodeCategory(c) == UnicodeCategory.Controlщоб вирішити, чи уникати цього, або люди, які не говорять про ASCII, не будуть дуже раді.
deerchao

Це залежить від ситуації, якщо отриманий рядок буде використовуватися в середовищі, що підтримує unicode чи ні.
Смайлик

Я додав input = input ?? string.Empty;як перший рядок методу, щоб я міг пройти nullта повернутися ""замість нульового посилання на виключення.
Енді

Приємно. Змініть додані лапки 'і тепер у вас є те, що Python дає вам з коробки repr(a_string):).
z33k

17

Відповідь Халлгрима - відмінна, але доповнення "+", рядки та відступи для мене порушили функціональність. Найпростіший спосіб подолати це:

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

Чудово працює. Я також додав один рядок перед тим, return literalщоб зробити його більш читабельним: literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
Боб

Додано це literal = literal.Replace("/", @"\/");для JSONфункціональності.
цікаво-ім’я-ось

Це на 100% прямо вперед і єдина правильна відповідь! Усі інші відповіді або не зрозуміли питання, або знову придумали колесо.
bytecode77

Сумно, не можна змусити цю роботу працювати в DOTNET CORE. Хтось має кращу відповідь?
sk

8

Ось невелике вдосконалення для відповіді Smilediver, це не дозволить уникнути всіх символів, що не належать до ASCII, але тільки вони справді потрібні.

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

Цікаве запитання.

Якщо ви не можете знайти кращого методу, його завжди можна замінити.
У випадку, якщо ви виберете це, ви можете скористатися цим списком послідовностей Escape :

  • \ '- одинарна цитата, необхідна для літературних символів
  • \ "- подвійна цитата, необхідна для рядкових літералів
  • \ - зворотний нахил
  • \ 0 - символ Unicode 0
  • \ a - Попередження (символ 7)
  • \ b - Резервна область (символ 8)
  • \ f - Форма подачі (символ 12)
  • \ n - новий рядок (символ 10)
  • \ r - повернення перевезення (символ 13)
  • \ t - горизонтальна вкладка (символ 9)
  • \ v - вертикальна цитата (символ 11)
  • \ uxxxx - послідовність відхилення Unicode для символу з шістнадцятковим значенням xxxx
  • \ xn [n] [n] [n] - послідовність відхилення Unicode для символу зі шістнадцятковим значенням nnnn (версія змінної довжини \ uxxxx)
  • \ Uxxxxxxxx - послідовність відхилення Unicode для символу з шістнадцятковим значенням xxxxxxxx (для генерації сурогатів)

Цей список можна знайти в C # Часто задаються питання Які послідовності втечі символів доступні?


2
Це посилання більше не працює, приклад з підручника, чому відповіді, що мають лише посилання, не перешкоджають.
Джеймс

Дуже правдиво, @James, але завдяки Джеймі Твеллсу інформація знову доступна: +1:
Нельсон Рейс,

5

Метод для цього є в пакеті Microsoft.CodeAnalysis.CSharp від Roslyn на nuget:

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

Очевидно, що цього не існувало під час початкового запитання, але це може допомогти людям, які потрапили сюди від Google.


3

Якщо конвенцій JSON достатньо для немальованих рядків, з яких ви хочете вийти, і ви вже використовуєте Newtonsoft.Jsonу своєму проекті (він має досить великі накладні витрати), ви можете використовувати цей пакет на зразок наступного:

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

Моя спроба додати ToVerbatim до прийнятої відповіді Hallgrim вище:

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

Відповідь Холлгрима була відмінною. Ось невеликий твір, якщо вам потрібно розібрати додаткові символи пробілу та рядки з регулярним виразом ac #. Мені це знадобилося у випадку серіалізованого значення Json для вставки в google аркуші і виникло проблеми, оскільки код вставляв вкладки, +, пробіли тощо.

  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

Я подаю власну реалізацію, яка обробляє nullзначення і повинна бути більш ефективною за рахунок використання таблиць пошуку масивів, ручного перетворення шістнадцяткових даних та уникнення switchоператорів.

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

Код:

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

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

Вихід:

    Hello
    World!

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

Це те, чого ти хочеш?


У мене є someString1, але він читається з файлу. Я хочу, щоб він відображався як someString2 після виклику якогось методу.
Hallgrim
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.