65.9K
CodeProject 正在变化。 阅读更多。
Home

快速且紧凑的 HTML/XML 扫描器/分词器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (30投票s)

2006年5月11日

BSD

2分钟阅读

viewsIcon

579016

downloadIcon

1984

HTML/XML 扫描器/分词器,也称为拉模式解析器

引言

所提出的代码是 HTML 和 XML 扫描器(或分词器)的实现。 假设您有一些 XML 或 HTML 文本,并且只需要在其中找到一些单词、标签或属性。 对于这种简单的任务,使用完整的“DOM 编译器”或类似的 SAX 解析器就太多了。 使用下面描述的 markup::scanner 就足够了。 markup::scanner 的特性包括

  1. 它在扫描时根本不分配任何内存。
  2. 它用纯 C++ 编写,不需要 STL 或任何其他工具包/库。
  3. 它很快。 我们设法达到了每秒扫描近 40 MB XML 的速度(当然,这取决于您拥有的硬件)。
  4. 它很简单。

如何使用

我认为最好的解释方法是展示一个例子。 首先,我们需要为扫描器声明输入流。 这是一个简单的基于字符串的流的示例

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("<html><body><p align=right" 
        " dir='rtl'> Begin & back </p>" "</body></html>");
    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,   // <tag ...
                    //     ^-- happens here
    TT_TAG_END,     // </tag>
                    //       ^-- happens here 
                    // <tag ... />
                    //            ^-- or here 
    TT_ATTR,        // <tag attr="value" >      
                    //                  ^-- happens here   
    TT_WORD,
    TT_SPACE,

    TT_DATA,        // content of following:

    TT_COMMENT_START, TT_COMMENT_END, // after "<!--" and "-->"
    TT_CDATA_START, TT_CDATA_END,     // after "<![CDATA[" and "]]>"
    TT_PI_START, TT_PI_END,           // after "<?" and "?>"
    TT_ENTITY_START, TT_ENTITY_END,   // after "<!ENTITY" and ">"
  
};

根据令牌的值,您可以使用 get_tag_name()get_value()get_attr_name() 来检索所需的信息。 这几乎就是您扫描 HTML/XML 所需的一切。

最后

给定的扫描器没有解决任何输入流编码问题。 XML 和 HTML 对此的处理方式不同。 对于您事先不知道输入编码的情况,一个总的想法是:您的输入流应该足够智能,能够动态切换输入的编码。 给定的扫描器最初是作为 HTMLayout SDK 的一部分创建的:一个轻量级的可嵌入的 HTML 渲染组件。

历史

  • 2006 年 5 月 11 日 - 初始版本
  • 2006 年 5 月 12 日 - 文章已移动
  • 2006 年 6 月 9 日 - 错误修复和一个新的 VS 2005 项目
  • 2007 年 10 月 10 日 - 下载已更新(错误修复)
© . All rights reserved.