如何解析转义序列?

时间:2016-10-31 17:17:46

标签: c# string parsing escaping

我正在为自己的标记编写解析器,我需要处理一些转义序列,但我不确定应该选择哪种策略。

特别是我心中有两个。

以下是foo \\\<bar baz的示例,其中有两个:\\\<

当我现在用char

扫描字符串char时
  1. 我应该检测反斜杠\,然后检查下一个字符是否是一个可以删除的字符或
  2. 我应该检查一下这个角色,然后回过头来看看它前面是否有反斜杠\
  3. 其中任何一个都有主要(dis)优势吗?

1 个答案:

答案 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变量应该是您跟踪所处状态的唯一方法。只需在枚举中添加更多状态即可。从字面上看,第三个十六进制数字和第四个十六进制数字是枚举中的不同状态值。

无意识地遵循这条规则,你可以编写令人惊讶的复杂状态机,智商低得惊人,注意力范围很大(我是证明)并且不会弄乱它。