In C#, can I convert a string value to a string literal, the way I would see it in code? I would like to replace tabs, newlines, etc. with their escape sequences.
If this code:
Console.WriteLine(someString); produces:
Hello World! I want this code:
Console.WriteLine(ToLiteral(someString)); to produce:
\tHello\r\n\tWorld!\r\n 16 Answers
A long time ago, I found this:
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(); } } } This code:
var input = "\tHello\r\n\tWorld!"; Console.WriteLine(input); Console.WriteLine(ToLiteral(input)); Produces:
Hello World! "\tHello\r\n\tWorld!" These days, Graham discovered you can use Roslyn's Microsoft.CodeAnalysis.CSharp package on NuGet:
private static string ToLiteral(string valueTextForCompiler) { return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(valueTextForCompiler, false); } 12Use Regex.Escape(String):
5Regex.Escape escapes a minimal set of characters (, *, +, ?, |, {, [, (,), ^, $,., #, and white space) by replacing them with their escape codes.
This is a fully working implementation, including escaping of Unicode and ASCII non-printable characters. It does not insert "+" signs like Hallgrim's answer.
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 '\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(); } Note that this also escapes all Unicode characters. If your environment supports them, you could change that part to escape only control characters:
// UTF16 control characters } else if (Char.GetUnicodeCategory(c) == UnicodeCategory.Control) { literal.Append(@"\u"); literal.Append(((int)c).ToString("x4")); } else { literal.Append(c); } 7A more structured approach, including all escape sequences for strings and chars, is:
It doesn't replace Unicode characters with their literal equivalent. It doesn't cook eggs, either.
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('\'')); } } 4There's a method for this in Roslyn's Microsoft.CodeAnalysis.CSharp package on NuGet:
private static string ToLiteral(string valueTextForCompiler) { return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(valueTextForCompiler, false); } Obviously, this didn't exist at the time of the original question, but it might help people who end up here from Google Search.
4Try:
var t = HttpUtility.JavaScriptStringEncode(s); 3public 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)]; } } 2Hallgrim's answer is excellent, but the "+", newline and indent additions were breaking functionality for me. An easy way around it is:
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; } } } 4Here is a little improvement for Smilediver's answer. It will not escape all no-ASCII characters, but only these are really needed.
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(); } } 0Interesting question.
If you can't find a better method, you can always replace.
In case you're opting for it, you could use this C# Escape Sequence List:
- \' - single quote, needed for character literals
- \" - double quote, needed for string literals
- \ - backslash
- \0 - Unicode character 0
- \a - Alert (character 7)
- \b - Backspace (character 8)
- \f - Form feed (character 12)
- \n - New line (character 10)
- \r - Carriage return (character 13)
- \t - Horizontal tab (character 9)
- \v - Vertical quote (character 11)
- \uxxxx - Unicode escape sequence for character with hex value xxxx
- \xn[n][n][n] - Unicode escape sequence for character with hex value nnnn (variable length version of \uxxxx)
- \Uxxxxxxxx - Unicode escape sequence for character with hex value xxxxxxxx (for generating surrogates)
This list can be found in the C# Frequently Asked Questions What character escape sequences are available?
2If JSON conventions are enough for the unescaped strings you want to get escaped and you already use Json.NET (Newtonsoft.Json) in your project (it has a pretty large overhead), you may use this package like the following:
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 + "\""); } } 1public 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(); } } 1My attempt at adding ToVerbatim to Hallgrim's accepted answer:
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; } 0Hallgrim's answer was excellent. Here's a small tweak in case you need to parse out additional white space characters and linebreaks with a C# regular expression. I needed this in the case of a serialized JSON value for insertion into Google Sheets and ran into trouble as the code was inserting tabs, +, spaces, 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; I submit my own implementation, which handles null values and should be more performant on account of using array lookup tables, manual hex conversion, and avoiding switch statements.
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 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(); } } 1Code:
string someString1 = "\tHello\r\n\tWorld!\r\n"; string someString2 = @"\tHello\r\n\tWorld!\r\n"; Console.WriteLine(someString1); Console.WriteLine(someString2); Output:
Hello World! \tHello\r\n\tWorld!\r\n 2