我正在为自己的标记编写解析器,我需要处理一些转义序列,但我不确定应该选择哪种策略。
特别是我心中有两个。
以下是foo \\\<bar baz
的示例,其中有两个:\\
和\<
。
当我现在用char
扫描字符串char时\
,然后检查下一个字符是否是一个可以删除的字符或\
?其中任何一个都有主要(dis)优势吗?
答案 0 :(得分:3)
不要这样做。选项#2非常糟糕,因为当你回顾前一个字符时,它是一个反斜杠,你怎么知道它是一个转义反斜杠,还是它真的逃脱了当前的角色?
你需要知道你在哪里。这样做的方法是状态机。如果您只是\r
,\t
,\n
,\"
和\\
,那么您可以使用非常简单的方法。像这样(fiddle here):
public static class StringExtensions
{
private enum UnescapeState
{
Unescaped,
Escaped
}
public static String Unescape(this String s)
{
var sb = new System.Text.StringBuilder();
UnescapeState state = UnescapeState.Unescaped;
foreach (var ch in s)
{
switch (state)
{
case UnescapeState.Escaped:
switch (ch)
{
case 't':
sb.Append('\t');
break;
case 'n':
sb.Append('\n');
break;
case 'r':
sb.Append('\r');
break;
case '\\':
case '\"':
sb.Append(ch);
break;
default:
throw new Exception("Unrecognized escape sequence '\\" + ch + "'");
// Finally, what about stuff like '\x0a'? That's a much more
// complicated state machine. When you see 'x' in Escaped state,
// you transition to UnescapeState.HexDigit0, then either
// UnescapeState.HexDigit1 or throw an exception, etc.
// Wicked fun to write.
}
state = UnescapeState.Unescaped;
break;
case UnescapeState.Unescaped:
if (ch == '\\')
{
state = UnescapeState.Escaped;
}
else
{
sb.Append(ch);
}
break;
}
}
if (state == UnescapeState.Escaped)
{
throw new Exception("Unterminated escape sequence");
}
return sb.ToString();
}
}
这里最重要的一点是,你可以拥有各种“累加器”变量,如sb
,或者如果你正在进行像\x0a
这样的十六进制字符转义,则可以累积十六进制数字,但是你没有标志。 state
变量应该是您跟踪所处状态的唯一方法。只需在枚举中添加更多状态即可。从字面上看,第三个十六进制数字和第四个十六进制数字是枚举中的不同状态值。
无意识地遵循这条规则,你可以编写令人惊讶的复杂状态机,智商低得惊人,注意力范围很大(我是证明)并且不会弄乱它。