Windows CE 2.10Windows CE .NET 4.1Windows CE .NET 4.2Windows CE 3.0Windows CE .NET 4.0Windows CE 2.11Windows XP 平板电脑版嵌入式Windows MobileVisual C++ 7.1Visual C++ 8.0Windows VistaVisual C++ 7.0Windows 2003Windows 2000Visual C++ 6.0Windows XP中级开发Visual StudioWindowsC++
快速且紧凑的 HTML/XML 扫描器/分词器
HTML/XML 扫描器/分词器,也称为拉模式解析器
引言
所提出的代码是 HTML 和 XML 扫描器(或分词器)的实现。 假设您有一些 XML 或 HTML 文本,并且只需要在其中找到一些单词、标签或属性。 对于这种简单的任务,使用完整的“DOM 编译器”或类似的 SAX 解析器就太多了。 使用下面描述的 markup::scanner
就足够了。 markup::scanner
的特性包括
- 它在扫描时根本不分配任何内存。
- 它用纯 C++ 编写,不需要 STL 或任何其他工具包/库。
- 它很快。 我们设法达到了每秒扫描近 40 MB XML 的速度(当然,这取决于您拥有的硬件)。
- 它很简单。
如何使用
我认为最好的解释方法是展示一个例子。 首先,我们需要为扫描器声明输入流。 这是一个简单的基于字符串的流的示例
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 日 - 下载已更新(错误修复)