Return 比较中的节点信息

Return node information during comparison

我在编写这段代码时得到了一些帮助。目前代码所做的是打印出文件中差异的 ID 号,即新的与旧的相比,添加、删除或保持不变。

然而我想要做的是 return 节点中仅出现在 new.xml 中的信息,而不仅仅是 ID(即标题、位置、日期)。

我可以从 Google 中找到的最佳猜测是使用(不知道如何实现):xpath->getAncestor

我现在的代码

#include <set>
#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>

#include "include/pugixml.hpp"

#define con(m) std::cout << m << '\n'
#define err(m) std::cerr << m << std::endl

using str_set = std::set<std::string>;

int main()
{
    pugi::xml_document doc;

    str_set a;
    doc.load_file("old.xml");

    // fill set a with just the ids from file a
    for(auto&& node: doc.child("site_entries").children("entry"))
        a.emplace(node.child("id").text().as_string());

    str_set b;
    doc.load_file("new.xml");

    // fill set b with just the ids from file b
    for(auto&& node: doc.child("site_entries").children("entry"))
        b.emplace(node.child("id").text().as_string());

    // now use the <algorithms> library

    str_set b_from_a;
    std::set_difference(a.begin(), a.end(), b.begin(), b.end()
        , std::inserter(b_from_a, b_from_a.begin()));

    str_set a_from_b;
    std::set_difference(b.begin(), b.end(), a.begin(), a.end()
        , std::inserter(a_from_b, a_from_b.begin()));

    str_set a_and_b;
    std::set_intersection(a.begin(), a.end(), b.begin(), b.end()
        , std::inserter(a_and_b, a_and_b.begin()));

    for(auto&& v: a)
        con("a       : " << v);

    con("");

    for(auto&& v: b)
        con("b       : " << v);

    con("");

    for(auto&& v: b_from_a)
        con("b_from_a: " << v);

    con("");

    for(auto&& v: a_from_b)
        con("a_from_b: " << v);

    con("");

    for(auto&& v: a_and_b)
        con("a_and_b : " << v);

    con("");
}

这是一个例子XML:

<?xml version="1.0" encoding="ISO-8859-1" ?> <site_entries> <entry> <id><![CDATA[946757316]]></id> <url><![CDATA[http://www.site.co.uk/cgi-bin/tr.cgi?tid=752276]]></url> <content><![CDATA[Specialized Dolce Sport 27 Speed]]></content> <title><![CDATA[Bike]]></title> <price><![CDATA[£600]]></price> <date><![CDATA[01-AUG-13]]></date> <display_reference><![CDATA[214683-50142933_370647]]></display_reference> <location><![CDATA[City of London]]></location> <category><![CDATA[Bike]]></category> </entry> <entry> <id><![CDATA[90007316]]></id> <url><![CDATA[http://www.site.co.uk/cgi-bin/tr.cgi?tid=70952276]]></url> <content><![CDATA[Giant Sport Offroad Bike]]></content> <title><![CDATA[Bike]]></title> <price><![CDATA[£100]]></price> <date><![CDATA[11-AUG-15]]></date> <display_reference><![CDATA[2146433-50142933_370647]]></display_reference> <location><![CDATA[City of London]]></location> <category><![CDATA[Bike]]></category> </entry> </site_entries>

我将有数十万个总结果和数万个添加条目,因此我正在寻找实现此目标的最有效方法。

您可以只将 xml_node 个对象放入地图中 - 而不是 std::set<std::string> 使用 std::map<std::string, pugi::xml_node>.

不过 possible/likely 使用 unordered_map 会更快。我会做这样的事情:

#include "pugixml.hpp"

#include <iostream>
#include <unordered_map>

struct string_hasher
{
    unsigned int operator()(const char* str) const
    {
        // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
        unsigned int result = 0;

        while (*str)
        {
            result += static_cast<unsigned int>(*str++);
            result += result << 10;
            result ^= result >> 6;
        }

        result += result << 3;
        result ^= result >> 11;
        result += result << 15;

        return result;
    }

    bool operator()(const char* lhs, const char* rhs) const
    {
        return strcmp(lhs, rhs) == 0;
    }
};

typedef std::unordered_map<const char*, pugi::xml_node, string_hasher, string_hasher> xml_node_map;

int main()
{
    pugi::xml_document doca, docb;
    xml_node_map mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry"))
        mapa[node.child_value("id")] = node;

    for (auto& node: docb.child("site_entries").children("entry"))
        mapb[node.child_value("id")] = node;

    for (auto& ea: mapa)
        if (mapb.count(ea.first) == 0)
        {
            std::cout << "Removed:" << std::endl;
            ea.second.print(std::cout);
        }

    for (auto& eb: mapb)
        if (mapa.count(eb.first) == 0)
        {
            std::cout << "Added:" << std::endl;
            eb.second.print(std::cout);
        }
}

与您的方法的显着差异:

  • unordered_map 让您降低 diff 的复杂性 - 现在是 O(N+M),而不是 O(NlogN + MlogM)
  • C 字符串的自定义哈希器避免分配不必要的内存

当然,您可以使用 std::unordered_map<std::string, pugi::xml_node> 进行简化 - 它可能会更慢,但更短。