【问题标题】:Parse simple html with pure C++用纯 C++ 解析简单的 html
【发布时间】:2014-04-15 00:33:01
【问题描述】:

在我的应用程序中,我需要在不使用尽可能少的外部库的情况下解析简单的 HTML 代码。我的 HTML 看起来像

<p> First Content is P </p><h2>Header</h2><p> Text under header </p>
<h2>Header 2</h2><p> Paragraph </p>
<h3>yep</h3><p> no </p>

我的 html 仅包含标签 p, h2, h3。我得到了以下结构:

struct Elements {
    std::string tag;
    std::string content;
};

std::vector<Elements> elems;

所以我的目标是在解析向量中的每个元素后应该包含这样的数据:

tag = "h2"
content = "Header"

tag = "p"
content = "First Content is P"

PP:我需要按照它们在 HTML 中呈现的顺序获取元素。

编辑:

我只是在 javascript 中做了这个,它工作正常,但我基本上不知道如何用 c++ 写下来:

var a = "<p> First Content is P </p><h2>Header</h2><p> Text under header </p>" +
    "<h2>Header 2</h2><p> Paragraph </p>" +
    "<h3>yep</h3><p> no </p>";

var output = [];

a.replace(/<\b[^>]*>(.*?)<\/(.*?)>/gmi, function(m, key, value) {
    output.push({
        tag: value,
        data: key
    });
})

/*
    output:
        { tag: "p", data: "First Content is P"},
        { tag: "h2", data: "Header" }
        .....
 */

【问题讨论】:

  • 你是指 HTML 还是 XHTML? HTML 允许标签没有结束元素等...
  • 您自己几乎从未编写过的两件事:加密和 HTML/XML 解析器。 You may find this interesting
  • 我所有的元素都有开始和结束标签。因为我只有 3 个标签,我可以对它们进行硬编码,我认为这可以通过某种正则表达式来实现吗?
  • “我认为这可以通过某种正则表达式来实现吗?” - 所以你确实知道如何开始......你为什么不试一试并告诉我们你在哪里卡住了? (以上所有警告的强制性回声)。说真的,如果你还没有准备好自己做,你必须使用图书馆。无论您自己(很快)编写的任何解决方案都可能非常脆弱。
  • 这是一个完全合法的学习使用 C++ 的迷你项目。谷歌“Lexer”和“Parser”(这有点矫枉过正,但给了你一个想法。如果你碰巧有 stroustrup 的书,并且版本没有改变这一点,他曾经在章节中有一个波兰语反向符号简单算术表达式解析器6,它处理更复杂的解析,但给你一个想法(虽然你不需要从你描述你的语言的方式中递归下降)。

标签: c++ api parsing vector


【解决方案1】:

只有这三个元素,并且没有缺少关闭标记。看起来好像标签上没有属性,甚至元素内也没有任何元素。标签内也没有空格。

那么你不是在解析 HTML。您正在解析一种特殊语言,它是 HTML 的子集(嗯,甚至不是真正的子集,因为您的文档没有经过验证)。

您可能有充分的理由不想使用 HTML 解析器来解析这种特殊语言。例如,完整的 HTML 解析器的代码非常庞大,否则可能不需要在您正在为其编写的非常小的嵌入式设备上。这很可能是一项学习任务,目标是让您操作字符串而不是以选择最佳工具来生成所需的输出。我假设您必须避免使用 HTML 库而不进一步考虑原因。

那么,如何解析这种特殊的语言呢?如何解析任何东西。鉴于我上面列出的所有限制,您可以非常简单地做到这一点:

  • 在三个子字符串&lt;p&gt;&lt;h2&gt;&lt;h3&gt; 中的任意一个的字符串中查找第一个实例。这是您的开始标签。
  • 找到对应关闭标签的第一个实例。
  • 之间的一切都是元素的内容。在您的示例中,您还可以在内容的每一端修剪空白。构造一个Elements 对象并将其添加到您的向量中(顺便说一句,请考虑使用单数类名,而不是复数)。
  • 对字符串的其余部分重复。

就是这样。你可以使用正则表达式来做到这一点,但我的一般感觉是,既然你你想用 C++ 来做,那么你不妨只用 C++ 来做。无需引入另一种语言,无论正则表达式的优点和局限性如何,它们肯定是另一种语言。

但是,我上面列出的额外限制可能无法保证。如果您以后想要支持标签内的空格怎么办?和属性?和 XML 命名空间?和cmets?然后你会希望你刚刚使用了一个 HTML 解析器。因此,您对 HTML 的固定琐碎子集所做的操作不同于您对重要子集或将来可能变得重要的子集所做的操作。

【讨论】:

    【解决方案2】:

    只是一个建议。 要加速解析器,请将 struct Elements 更改为类似

    struct Node { const char * ptrToNodeStart; int nodeLen; Entity() ... etc}
    
    struct Elements {
    Node tag;
    Node content; };
    

    主要思想是避免为标签和内容分配内存,因为您已经在内存中拥有整个文档。只需将其保留在那里并使用指针进行操作。它要快得多。使用指针,解析过程将在单个分配完成之前结束。 当您的解析器运行文档时,它将创建新节点(将从预先分配的池中获取)并将当前 ptr 放入 Node::ptrToNodeStart。当新节点出现(或当前节点关闭)时,您修复 Node::nodeLen 并使用 Element 完成。这就是想法。 struct Elements 的严重问题,它不适合 HTML 结构,因为 HTML 节点通常包含其他节点,因此需要嵌套 Elements。 即使市场上已经有大量的解析器,解析 HTML 也是一项有趣的任务。祝你好运。

    【讨论】:

    • 在我的 HTML 代码中,我没有嵌套标签。谢谢你的建议,我现在就试试。
    猜你喜欢
    • 1970-01-01
    • 2011-02-13
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    相关资源
    最近更新 更多