在处理XML或HTML文本时,如果只需要找到某个词、标签或属性,使用完整的DOM编译器或SAX解析器可能过于复杂。此时,使用一个轻量级的标记扫描器是一个更好的选择。下面介绍的markup::scanner就是这样一个工具,它具有以下特点:
最好的解释方式是通过示例。首先,需要为扫描器声明一个输入流。以下是一个基于字符串的简单流示例:
        struct str_istream : public markup::instream {
            const char* p;
            const char* end; 
            str_istream(const char* src) : p(src), end(src + strlen(src)) {}
            virtual wchar_t get_char() {
                return p < end ? *p++ : 0;
            }
        };
    
以上代码定义了一个简单的字符串输入流。接下来,将编写一个程序,打印出输入HTML中的所有标记:
        int main(int argc, char* argv[]) {
            str_istream si(
                " Begin & back 
"
                ""
            );
            markup::scanner sc(si);
            bool in_text = false;
            while (true) {
                int t = sc.get_token();
                switch (t) {
                    case markup::scanner::TT_ERROR:
                        printf("ERROR\n");
                        break;
                    case markup::scanner::TT_EOF:
                        printf("EOF\n");
                        goto FINISH;
                    case markup::scanner::TT_TAG_START:
                        printf("TAG START:%s\n", sc.get_tag_name());
                        break;
                    case markup::scanner::TT_TAG_END:
                        printf("TAG END:%s\n", sc.get_tag_name());
                        break;
                    case markup::scanner::TT_ATTR:
                        printf("\tATTR:%s=%S\n", sc.get_attr_name(), sc.get_value());
                        break;
                    case markup::scanner::TT_WORD:
                    case markup::scanner::TT_SPACE:
                        printf("{%S}\n", sc.get_value());
                        break;
                }
            }
            FINISH:
            printf("--------------------------\n");
            return 0;
        }
    
如所见,主要的工作方法是markup::scanner::get_token()。它扫描输入流并返回markup::scanner::token_type的值。
        enum token_type {
            TT_ERROR = -1,
            TT_EOF = 0,
            TT_TAG_START,
            TT_TAG_END,
            TT_ATTR,
            TT_WORD,
            TT_SPACE,
            TT_DATA,
            TT_COMMENT_START, TT_COMMENT_END,
            TT_CDATA_START, TT_CDATA_END,
            TT_PI_START, TT_PI_END,
            TT_ENTITY_START, TT_ENTITY_END,
        };