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
구글에서 주제를 찾았습니다. 이것은 .net이 우리를 위해 할 수있는 것들을 재창조하는 데있어 최고의 것이되어야합니다
— Andy Morris

16
좋은 점이지만 더 긴 문자열의 경우 "+"연산자, 줄 바꿈 및 들여 쓰기가 삽입됩니다. 끄는 방법을 찾지 못했습니다.
— Timwi

2
역수는 어떻습니까? 텍스트가 포함 된 이스케이프 시퀀스 파일이있는 경우 특수 문자가 ASCII 코드로 이스케이프 처리됩니까? 원시 버전을 생성하는 방법은 무엇입니까?
— Luciano

1
다음을 실행하는 경우 : void Main () {Console.WriteLine (ToLiteral ( "test \"\ '\\\ 0 \ a \ b \ f \ n \ r \ t \ v \ uaaaa \\\ blah "));} 로니 오버 비 (Ronnie Overby)는 \ f를 가리키고 다른 것은 \ a와 \ b입니다
— costa

4
그대로 @"..."리터럴 ( ) 리터럴을 출력하는 방법이 있습니까?
— rookie1024

39

무엇에 대한 Regex.Escape (문자열) ?

Regex.Escape는 이스케이프 코드로 대체하여 최소 문자 세트 (\, *, +,?, |, {, [, (,), ^, $,., # 및 공백)를 이스케이프합니다.


6
왜 이것이 아래인지 +1 모르겠다. 다른 답변은 너무 장황하고 바퀴를 재발견하는 것처럼 보입니다
— Adriano Carneiro

40
이것은 OP가 요구하는 것이 아닙니다. 문자열 리터럴을 반환하지 않으며 Regex 특수 문자가 이스케이프 된 문자열을 반환합니다. 이 될지는 Hello World?으로 Hello World\?, 그러나 그것은 잘못된 문자열 리터럴입니다.
— atheaos

2
@atheaos에 동의합니다. 이것은 매우 다른 질문에 대한 훌륭한 답변입니다.
— hypehuman

5
+1 OP의 질문에 대답하지는 못했지만이 질문을 만났을 때 내가 찾은 것이 었습니다. :)
— GazB 2016 년

필요에 따라 작동하지 않습니다. 정규식 특수 문자는 동일하지 않습니다. 예를 들어 \ n에서 작동하지만 공백이 있으면 C #이 수행하지 않는 "\"로 변환됩니다.
— Ernesto

25

편집 : strings 및 chars에 대한 모든 이스케이프 시퀀스를 포함하여보다 체계적인 접근 방식 .
유니 코드 문자를 해당 문자로 대체하지 않습니다. 계란도 요리하지 않습니다.

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
위의 솔루션보다 잘 작동하며 다른 이스케이프 시퀀스를 쉽게 추가 할 수 있습니다.
— Arno Peters

받아 들인 대답의 Verbatim은 저를 실책하게 만들었습니다. 이것은 내 목적으로 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"문자열 리터럴을 포함합니다.
— Paul

2
@Paul 원하는 것은 질문이하는 것과 반대입니다. 이것은 귀하의 설명에 따라 질문에 대답하므로 작동 합니다.
— Fund Monica의 소송

프론트 엔드에서 활성 디렉토리 이름을 피하는 것이 유용하다는 것을
— 알았습니다.

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 개의 백 슬래시와 2 개의 음성 표시가있는 이유는 무엇입니까?
— James Yeoman

정답은 @JamesYeoman입니다. 정규식 패턴을 이스케이프해야하기 때문입니다.
— 알리 Mousavi Kherad

18

유니 코드 및 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

결과 문자열이 유니 코드를 지원하는 환경에서 사용되는지 여부에 따라 상황에 따라 다릅니다.
— Smilediver

null 참조 예외 대신 input = input ?? string.Empty;전달 null하고 다시 얻을 수 있도록 메서드의 첫 번째 줄로 추가 했습니다 "".
— Andy

좋은. 묶는 따옴표를 변경 '하면 이제 파이썬에서 제공하는 내용이 repr(a_string):)로 표시됩니다.
— z33k

17

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

잘 작동합니다. 또한 return literal읽기 쉽도록 한 줄을 추가 했습니다. literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
— Bob

기능을 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

흥미로운 질문입니다.

더 좋은 방법을 찾을 수 없다면 언제든지 바꿀 수 있습니다.
당신이 그것을 선택한다면, 당신은이 C # 탈출 시퀀스 목록을 사용할 수 있습니다 :

  • \ '-문자 리터럴에 필요한 작은 따옴표
  • \ "-큰 따옴표, 문자열 리터럴에 필요
  • 백 슬래시
  • \ 0-유니 코드 문자 0
  • \ a-경고 (문자 7)
  • \ b-백 스페이스 (문자 8)
  • \ f-용지 공급 (문자 12)
  • \ n-줄 바꾸기 (문자 10)
  • \ r-캐리지 리턴 (문자 13)
  • \ t-가로 탭 (문자 9)
  • \ v-세로 따옴표 (문자 11)
  • \ uxxxx-16 진수 값이 xxxx 인 문자의 유니 코드 이스케이프 시퀀스
  • \ xn [n] [n] [n]-16 진수 값이 nnnn 인 문자의 유니 코드 이스케이프 시퀀스 (가변 길이 버전 \ uxxxx)
  • \ Uxxxxxxxx-16 진수 값이 xxxxxxxx 인 문자의 유니 코드 이스케이프 시퀀스 (대리인 생성 용)

이 목록은 C # FAQ에서 찾을 수 있습니다. 어떤 문자 이스케이프 시퀀스를 사용할 수 있습니까?


2
링크 전용 답변이 권장되지 않는 교과서의 예인이 링크는 더 이상 작동하지 않습니다.
— James

@James는 매우 사실이지만 Jamie Twells 덕분에 정보를 다시 이용할 수 있습니다. +1 :
— Nelson Reis

5

nuget 의 Roslyn 's Microsoft.CodeAnalysis.CSharp 패키지 에는이를위한 방법이 있습니다 .

    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

위 의 Hallgrim의 대답에 ToVerbatim을 추가하려는 시도는 다음과 같습니다.

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의 대답은 훌륭했습니다. ac # 정규 표현식으로 추가 공백 문자와 줄 바꿈을 구문 분석해야 할 경우를 대비하여 약간의 조정이 있습니다. Google 시트에 삽입하기 위해 직렬화 된 Json 값의 경우이가 필요했으며 코드가 탭, +, 공백 등을 삽입 할 때 문제가 발생했습니다.

  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값 을 처리 하고 배열 조회 테이블 사용, 수동 16 진수 변환 및 회피 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.