C#文字列値をエスケープされた文字列リテラルに変換できますか


195

C#では、文字列値を文字列リテラルにコードで変換できますか?タブや改行などをエスケープシーケンスに置き換えたい。

このコードの場合:

Console.WriteLine(someString);

生成する:

Hello
World!

このコードが欲しい:

Console.WriteLine(ToLiteral(someString));

生産する:

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

回答:


180

私はこれを見つけました:

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 "));}これはいくつかのエスケープを処理しないことに気づくでしょう。RonnieOverbyが\ fをポイントし、他は\ aおよび\ b
costa

4
逐語的(@"...")リテラルを出力する方法はありますか?
rookie1024 2016年

38

何についてRegex.Escape(文字列)

Regex.Escapeは、最小限の文字セット(\、*、+、?、|、{、[、(、)、^、$、。、#、および空白)をエスケープコードに置き換えることでエスケープします。


6
+1なぜこれがずっと下にあるのかわからない。他の答えは冗長すぎて、車輪を再発明するように見えます
Adriano Carneiro 14

39
これはOPが求めているものではありません。文字列リテラルを返さず、正規表現の特殊文字をエスケープした文字列を返します。これはに変わりますHello World?Hello World\?、これは無効な文字列リテラルです。
atheaos

1
私は@atheaosに同意します。これは非常に異なる質問に対する素晴らしい答えです。
hypehuman 2015

5
+1はOPの質問に完全には答えませんが、この質問に出くわしたときに私が(したがって、おそらく他の人も)探していたものでした。:)
GazB 2016年

これは必要に応じて機能しません。正規表現の特殊文字は同じではありません。たとえば、\ nで機能しますが、スペースがある場合、「\」に変換されます。これは、C#が行うことではない...
Ernesto

24

編集: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
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 2017年

正解です。@ JamesYeomanは正規表現パターンをエスケープする必要があるためです。
Ali Mousavi Kherad

18

試してください:

var t = HttpUtility.JavaScriptStringEncode(s);

動作しません。"abc \ n123"(引用符なし、8文字)がある場合、 "abc" + \ n + "123"(7文字)が必要です。代わりに、 "abc" + "\\" + "\ n123"(9文字)を生成します。スラッシュが2倍になり、エスケープ文字ではなく "\ n"の文字列リテラルが2文字として含まれていることに注意してください。
Paul

2
@Paulあなたが望むのは、しかし、質問が尋ねていることの反対です。これは、あなたの説明によると、質問に答えるため、機能します。
モニカの訴訟に資金

フロントエンドでActive Directory名をエスケープするのにこれが便利であることがわかりました
chakeda

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それをエスケープするかどうかを決定するために使用する必要があります。
deerchao 2013年

結果の文字列がUnicodeをサポートする環境で使用されるかどうかは、状況によって異なります。
スマイルダイバー、2013年

input = input ?? string.Empty;メソッドの最初の行として追加したので、null参照例外の代わりに渡しnullたり戻ったりすることができました""
アンディ

いいね。囲みの引用符をに変更する'と、Pythonが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;
        }
    }
}

よく働く。前に私はまた、1行を追加してreturn literal、それをより読みやすくするために、: literal = literal.Replace("\\r\\n", "\\r\\n\"+\r\n\"");
ボブ・

機能性のliteral = literal.Replace("/", @"\/");ためにこれを追加しましたJSON
興味深い名前-ここ

これは100%単純明快で唯一の正解です。他のすべての答えは、質問を理解しなかったか、車輪を再発明しました。
bytecode77

悲しいことに、これをDOTNET COREで動作させることはできません。誰かがより良い答えを持っていますか?
sk 2018

8

これはスマイルダイバーの答えの少しの改善です、それはすべての非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-Unicode文字0
  • \ a-警告(文字7)
  • \ b-バックスペース(文字8)
  • \ f-フォームフィード(文字12)
  • \ n-改行(文字10)
  • \ r-キャリッジリターン(13文字目)
  • \ t-水平タブ(文字9)
  • \ v-縦引用(文字11)
  • \ uxxxx-16進値xxxxの文字のUnicodeエスケープシーケンス
  • \ xn [n] [n] [n]-16進値nnnnの文字のUnicodeエスケープシーケンス(可変長バージョンの\ uxxxx)
  • \ Uxxxxxxxx-16進値xxxxxxxxの文字のUnicodeエスケープシーケンス(サロゲートの生成用)

このリストは、C#のよくある質問に あります。使用できる文字エスケープシーケンスは何ですか。


2
このリンクは機能しなくなりました。リンクのみの回答が推奨されない理由の教科書の例です。
ジェームズ

@Jamesのとおりですが、Jamie Twellsのおかげで情報が再び利用可能になります:+1:
Nelson Reis

5

nugetのRoslynの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

ハルグリムの答えは素晴らしかった。次に、c#正規表現を使用して追加の空白文字と改行を解析する必要がある場合の小さな調整を示します。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 2008年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.