У меня есть следующий файл XML:
<?xml version="1.0" encoding="utf-8"?>
<gexf>
<graph>
<nodes>
<node id="0" label="0" start="0" end="25"/>
<node id="1" label="1" start="1"/>
<node id="2" label="2" start="2"/>
...
</nodes>
<edges>
<edge id="0" source="0" target="1" start="7" end="19"/>
<edge id="1" source="0" target="2" start="8" end="20"/>
...
</edges>
</graph>
</gexf>
Я хочу удалить start
а также end
атрибуты от края с source="0"
а также target="1"
,
Способ, которым я пытался сделать это, в следующем коде. Предполагая, что файл XML назван ptree_test.gexf
Я прочитал его, нашел правильный край в дереве, а затем попытался использовать erase
избавиться от атрибутов.
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main(int argc, char *argv[]) {
ptree pt;
read_xml("ptree_test.gexf", pt);
// Now find edge (0, 1) and delete the start and end attributes
ptree edge;
int id1, id2;
id1 = 0;
id2 = 1;
for(auto &e : pt.get_child("gexf.graph.edges")) {
int s, t;
s = e.second.get<int>("<xmlattr>.source");
t = e.second.get<int>("<xmlattr>.target");
// Check if this is the correct edge
// Two checks because it can be reversed
if((id1 == s && id2 == t) || (id1 == t && id2 == s)) {
edge = e.second;
break;
}
}
for(auto & attr : edge.get_child("<xmlattr>")) {
if(attr.first == "end" || attr.first == "start") {
edge.erase(attr.first);
}
}
write_xml(std::cout, pt);
return 0;
}
Это не работает. Это не удаляет атрибут. На самом деле, если я вставлю отладочный оператор, который печатает возврат edge.erase(attr.first)
это показывает 0
,
Прежде чем ответить, я хотел бы еще раз отговорить вас от использования Boost.PropertyTree в качестве быстрой и грязной системы обработки XML. пожалуйста использовать настоящий XML-парсер; Есть из чего выбирать, и некоторые из них достаточно эффективны и требуют минимального обслуживания зависимостей.
Во всяком случае, ваша проблема исходит от ваше использование erase
. Вы пытаетесь удалить элемент из списка, который вы перебираете. Это не сработает. Не без специального кодирования для вашего цикла.
Так что вы не можете использовать цикл for, основанный на диапазоне. Вы должны использовать реальный цикл for для итераторов.
auto &children = edge.get_child();
for(auto &attrIt = children.begin(); attrIt != children.end();)
{
auto &attr = *attrIt;
//Do stuff here.if(attr.first == "end" || attr.first == "start")
attrIt = children.erase(attrIt);
else
++attrIt;
}
Основная проблема заключается в том, что вы делаете копию поддерева в этой строке:
edge = e.second;
а затем изменить это копия вместо оригинала. Позже, как сказал @NicolBolas, вам нужен erase
, Полный код выглядит так:
int main(){
boost::property_tree::ptree pt;
read_xml("ptree_test.gexf", pt, boost::property_tree::xml_parser::trim_whitespace);
int id1, id2;
id1 = 0;
id2 = 1;
for(auto &e : pt.get_child("gexf.graph.edges")) {
int s, t;
s = e.second.get<int>("<xmlattr>.source");
t = e.second.get<int>("<xmlattr>.target");
// Check if this is the correct edge
// Two checks because it can be reversed
if((id1 == s && id2 == t) || (id1 == t && id2 == s)){
auto &children = e.second.get_child("<xmlattr>");
for(auto attrIt = children.begin(); attrIt != children.end(); ++attrIt){
if(attrIt->first == "end" || attrIt->first == "start")
attrIt = children.erase(attrIt);
}
break; // maybe shouldn't be here to keep looking?
}
}
write_xml("ptree_test_copy.gexf", pt, std::locale(), bpt::xml_writer_settings<std::string>{'\t', 1});
}