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>
进行简化 - 它可能会更慢,但更短。
我在编写这段代码时得到了一些帮助。目前代码所做的是打印出文件中差异的 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>
进行简化 - 它可能会更慢,但更短。