这些被称为转义序列。您可以咨询the grammar 以查看需要在字符串文字中转义哪些字符。基本上,您可以使用其 Unicode 字符转义序列来转义任何字符。
\u hex_digit hex_digit hex_digit hex_digit
例如:将 U+000D 替换为 \u000d 作为回车符。
如果你想保持字符串简短,那么有些不需要需要转义。 确实需要转义的是:
-
" (U+0022)
-
\ (U+005C)
- 回车符 (U+000D)
- 换行符 (U+000A)
- 下一行字符 (U+0085)
- 行分隔符 (U+2028)
- 段落分隔符 (U+2029)
其他所有内容都可以按字面意思插入。
如果您还只想允许对源文件进行 ASCII 编码,那么您可以更加严格地限制字面表示的字符。您可能想要非常严格。
让自己成为一个函数来决定一个字符是否应该被转义。您可能希望从以下函数开始:
public static bool IsSafeForLiteral(char ch) =>
ch < 127
&& ch != '\u0022' // double quote
&& ch != '\u005c' // backslash
&& ch != '\u000d' // carriage return
&& ch != '\u000a' // line feed
&& (
Char.IsLetterOrDigit(ch)
|| Char.IsPunctuation(ch)
|| Char.IsSymbol(ch)
|| (ch == ' ')
);
然后使用此测试构造一个函数,将字符串转换为字符串文字的 C# 源代码。
public static string ToSourceStringLiteral(string str)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char c in str) {
if (IsSafeForLiteral(c)) {
sb.Append(c);
} else {
sb.AppendFormat(@"\u{0:X4}", (int)c);
}
}
sb.Append("\"");
return sb.ToString();
}
如果您真的很喜欢将回车显示为 \r 而不是 \u000d,那么您还必须编写所有这些转义序列。
一种方法是制作一个字符字典来替换并应用它。
public static Dictionary<char, string> CSharpSpecialEscapes = new Dictionary<char, string>() {
{ '\u0000', @"\0" },
{ '\u0007', @"\a" },
{ '\u0008', @"\b" },
{ '\u0009', @"\t" },
{ '\u000a', @"\n" },
{ '\u000b', @"\v" },
{ '\u000c', @"\f" },
{ '\u000d', @"\r" },
{ '\u001b', @"\e" },
{ '\u005c', @"\\" }
};
public static string ToSourceStringLiteral(this string str)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char c in str) {
if (CSharpSpecialEscapes.TryGetValue(c, out string replacement)) {
sb.Append(replacement);
} else if (IsSafeForLiteral(c)) {
sb.Append(c);
} else {
sb.AppendFormat(@"\u{0:X4}", (int)c);
}
}
sb.Append("\"");
return sb.ToString();
}
根据性能要求,您还可以使用 0..127 范围内的所有替换项预先填充一个数组并直接使用它,尽管此时源代码开始看起来不太可维护。我推荐我上面写的内容,因为它是描述性的(与字符串转义序列的定义方式相匹配,而不是最佳效率)。
我还让这个版本在开头和结尾添加了引号。如果您不想要 sb.Append("\""); 行,您可以轻松删除它们。