C++ 在遍历boost属性树时,变量变为空

C++ 在遍历boost属性树时,变量变为空,c++,json,c++11,boost,tree,C++,Json,C++11,Boost,Tree,我知道我似乎有一个回答自己问题的习惯,但到目前为止,我一直坚持这一点,需要一些帮助 我编写了一些代码,将json格式的文件加载到类系统中。我把所有的代码都放在这里: 简而言之,我正在尝试做的是: 首先,我创建了一些代码,可以读入“需求”文件,并使用class.h中的需求类存储它 同样,我可以以人类可读的格式输出,以筛选或将其存储在json文件中。这很有效 然后,我希望能够读取JSON文件,并再次使用相同的需求对象将其存储在内存中,但这并不是很好(到目前为止) 现在的主要问题是我遍历属性树的部分

我知道我似乎有一个回答自己问题的习惯,但到目前为止,我一直坚持这一点,需要一些帮助

我编写了一些代码,将json格式的文件加载到类系统中。我把所有的代码都放在这里:

简而言之,我正在尝试做的是: 首先,我创建了一些代码,可以读入“需求”文件,并使用class.h中的需求类存储它 同样,我可以以人类可读的格式输出,以筛选或将其存储在json文件中。这很有效

然后,我希望能够读取JSON文件,并再次使用相同的需求对象将其存储在内存中,但这并不是很好(到目前为止)

现在的主要问题是我遍历属性树的部分,这主要是在这个递归函数中完成的:

    void display(const int depth, const boost::property_tree::ptree& tree, Requirement * cur_requirement, std::vector<Requirement> &requirements) { 
    unsigned int count;
   std::string label,level,description;
   boost::property_tree::ptree kids = tree.get_child("");
    bool godown = false;
    for (const auto& v : kids) { // v is of type ptree::value_type
        std::cout << std::string("").assign(depth+1,'#') << " ";
        std::string nodestr = tree.get<std::string>(v.first);  
        //std::cout << v.first << " = " << nodestr << std::endl;
        if (v.first == "label") {
            label = nodestr;
            std::cout << "lbl: " << label << std::endl;
        }
        else if(v.first == "level") {
            //std::cout << "LABEL!";
                level = nodestr;
                std::cout << "lvl: " << level << std::endl;
        }
        else if(v.first == "description") {
                description = nodestr;
                std::cout << "dsc: " << description << std::endl;
        }
        else if(v.first == "children") { //going down, store stuff first
            if(depth == 0) { //zero depth
                std::cout << "zero depth...";
                requirements.emplace_back(level, description, label,cur_requirement);
                cur_requirement = &requirements.back();
            }
            else { //one or higher depth
                std::cout << "at depth " << depth << "..." << std::flush; 
                cur_requirement->children.emplace_back(level,description,label,cur_requirement->parent);
                cur_requirement = &cur_requirement->children.back();
            }
            std::cout << "going down" << std::endl;
            //cur_requirement = &cur_requirement->children.back();
            display(depth+1, v.second, cur_requirement,requirements);
        }
        else if(v.first == "") {
            std::cout << "empty v.first ... level: " << level << std::endl;
            if(depth == 0) { //zero depth
                std::cout << "store at zero depth...";
                requirements.emplace_back(level, description, label,cur_requirement);
                cur_requirement = &requirements.back();
            }
            else { //one or higher depth
                std::cout << "store at depth " << depth << " : " << level << "--" << description << std::flush; 
                cur_requirement->children.emplace_back(level,description,label,cur_requirement->parent);
                //cur_requirement = &cur_requirement->children.back();
            }
            std:: cout << " going to next " << std::endl;
            //cur_requirement = &cur_requirement->children.back();
            display(depth, v.second, cur_requirement,requirements);
        }
        else {
            std:: cout << "what else..." << std::endl;
            }
     // v.first is the name of the child
    // v.second is the child tree
    }
};  
其中大部分是有道理的,而且大部分似乎都有效,但有一件事让我困惑。属性树的组织方式是在每个“子”之前以及数组元素之间都有一个“空”节点。(如果我错了,请纠正我,我对property tree不太熟悉)

因此,在我遇到一个“children”或“”(empty)元素之后,我想存储我以前收集的数据,存储在变量级别、描述和标签中

有趣的是,当元素为“children”时,这就像一个符咒,然而,当元素为“”时,突然变量为空,即使变量没有重新初始化,我也没有深入属性树,我只迭代到for循环中的下一个“kid”

因此,我预计输出如下:

## lvl: should
## dsc: be listening to spaces as well
## lbl: lisspace
## empty v.first ... level: should
store at depth 1 : should -- be listening to spaces as well going to next 
最后一行(由生成)

我试了15分钟。我想不出你想达到什么目的。 请参见更新

注释

  • 应初始化当前要求
  • 您可以在此处调用UB:

    requirements.emplace_back(level, description, label, cur_requirement);
    cur_requirement = &requirements.back();
    
    您正在放置的需求中存储指向向量元素的指针。但是,放置可能会重新分配,使所有指针和迭代器无效

重新考虑您的数据模型(更喜欢值语义?使用具有稳定迭代器的容器?预先保留?)

  • 请参见:
更新

下面是我清理
display
函数(我将其重命名为
parse_json
,因为它就是这样做的):

输出为:

at depth 0... going down
at depth 0... going down
at depth 1... going down
at depth 2... going down
at depth 2... going down


; debug: level: should description:very well performance wise label: goperf
; debug:    level: should description:be listening to spaces as well label: lisspace
; debug:    level: will description:a lot of levels back down again label: 
; debug:        level: empty description:empty label: empty
; debug:            level: can description:skip all the way back here label: skiphere
; debug:            level: can description:take three linestr label: threelines
{
    "level":"should", 
    "description":"very well performance wise",
    "label":"goperf", "children":[

    {
        "level":"should", 
        "description":"be listening to spaces as well",
        "label":"lisspace"
    },
    {
        "level":"will", 
        "description":"a lot of levels back down again", "children":[

        {
            "level":"empty", 
            "description":"empty",
            "label":"empty", "children":[

            {
                "level":"can", 
                "description":"skip all the way back here",
                "label":"skiphere"
            },
            {
                "level":"can", 
                "description":"take three linestr",
                "label":"threelines"
            }]
        }]
    }]
}

请注意,代码的大小大约是原来的一半:)

我最后也链接到main.cpp(现在称为readreq.cpp)中,虽然我没有很好地实现递归,但它确实做到了。为了能够有一个基本的需求列表(而不仅仅是一个根目录),代码也在github上,我们还必须改变其他一些事情 基本变化:

#include <fstream>
#include <iostream>
#include "class.h" 
#include <vector>
#include <string>
//#include <regex>
#include <boost/regex.hpp>
#include <iterator>
#include <algorithm>
#include <math.h>

//how many spaces are interpreted as one tab or level
#define SPACES 8 
#define LINEBUFSIZE 500 

void parse_req(unsigned int cur_depth, std::ifstream &f, std::list<Requirement> &reqlist,unsigned int linenr=0) {
    std::string line;
    unsigned int count = 0;
    unsigned int depth = 0;
    Requirement * cur(nullptr);
    boost::regex re("\\t+|\\s{2,}"); //regex split parameter (one or more tabs or 2 or more spaces)

    std::list<Requirement>* lptr(&reqlist);
    while (std::getline(f, line))   {
        linenr++;
        //  std::cout << "(" << linenr << "): " << line; 
        depth = line.find_first_not_of("    "); //find first none space or tab character


        boost::sregex_token_iterator i(line.begin()+depth, line.end(), re, -1); //split line by tabs and spaces
        boost::sregex_token_iterator j;
        count = 0; //reset count
        if (depth == cur_depth) {
            lptr->emplace_back(lptr->back().parent);
            cur = &lptr->back();
        }
        else if (depth > cur_depth) { //go deeper
            cur_depth = depth;
            lptr = &lptr->back().children;
            lptr->emplace_back(cur);
            cur = &lptr->back();
        }
        else if (depth < cur_depth) { //go down
            //lptr = cur->parent;
            while (cur_depth > depth) { 
                cur_depth--;
                std::cout << "deeper : " << cur_depth << std::endl << std::flush;
                lptr = &(lptr->back().parent->parent->children);
            }
            if(cur_depth != 0) {
                lptr->emplace_back(lptr->back().parent);
                cur = &lptr->back();
            }
            else {
                reqlist.emplace_back(nullptr);
                cur = &reqlist.back();
            }
        }
        while(i != j) { //for all splitted parts
            switch(count) {
                case 0:
                    cur->level  =*i++;
                    //std::cout << "lvl: " << cur->level << " ";
                    break;
                case 1:
                    cur->description = *i++;
                    //std::cout << "dsc: " << cur.description << std::endl;
                    break;
                case 2:
                    cur->label = *i++;
                    break;
                default:
                    *i++;
                    break;
            }
            count++;
        }
        if (count < 2) { //too less arguments
            std::cout << "ERROR(" << linenr << "): nead at least two parts for an requirement (level and description), less than two found, halting." << std::endl;
            break;
        }
        if (count > 3) { //too much arguments
            std::cout << "WARNING(" << linenr << "): More then three arguments found, ignoring fourth or more argument" << std::endl;
        }
    }
}
int main(int argc, char *argv[]) {
//  class Requirement req("will",  "do good work", "good");
    if ( argc != 3 ) // argc should be 2 for correct execution
        // We print argv[0] assuming it is the program name
        std::cout<<"usage: "<< argv[0] <<" <filename input> <filename output>\n";
     else {
        std::ifstream file(argv[1]); //try to open file
        if(!file.is_open()) { //if we can't
            std::cout << "Could not open file" ;
        }
        else { //sucesfully opened file
            std::list<Requirement> requirements;
            parse_req(0,file,requirements);

            std::ofstream outfile(argv[2]); //try to open file
            outfile << "{ \"requirements\":[";
            for (Requirement req : requirements) {

                req.print();
                req.print_json(outfile);
                if (requirements.back().description != req.description) {
                    outfile << ",";
                }
            }
            outfile << "]}";
            std::cout << "success!" << std::endl;
            }

        }

    return 0;
}
#包括
#包括
#包括“h类”
#包括
#包括
//#包括
#包括
#包括
#包括
#包括
//有多少空间被解释为一个选项卡或级别
#定义空间8
#定义LINEBUFSIZE 500
无效解析请求(无符号整数当前深度,标准::ifstream&f,标准::list&reqlist,无符号整数linenr=0){
std::字符串行;
无符号整数计数=0;
无符号整数深度=0;
要求*cur(空PTR);
boost::regex re(“\\t+|\\s{2,}”);//regex分割参数(一个或多个制表符或2个或多个空格)
标准::列表*lptr(&reqlist);
while(std::getline(f,line)){
linenr++;
//std::cout cur_depth){//深入
cur_depth=深度;
lptr=&lptr->back();
lptr->emplace_back(cur);
cur=&lptr->back();
}
否则,如果(深度<当前深度){//向下
//lptr=cur->parent;
而(cur_depth>depth){
电流深度--;
std::cout emplace_back(lptr->back().parent);
cur=&lptr->back();
}
否则{
需求列表。安置后(空PTR);
cur=&reqlist.back();
}
}
而(i!=j){//对于所有拆分的部分
开关(计数){
案例0:
cur->level=*i++;

//std::您是否尝试过使用调试器来查看«misterious Behavior»首先发生在何处?到目前为止,我注意到,当JSON数组元素没有“子元素”时,就会发生这种行为,这意味着每当属性树遇到“空”v时,它会首先(将一个节点声明给下一个元素)突然“空”标签、级别和描述字符串变量原因不明。至于调试器,我来自微控制器的C编程和其他基本编程,所以不,我会有一个好的,但我不确定在哪里寻找…感谢尝试,我试图实现的是将json格式的文件存储到嵌套的requireme中NTS,但是使用C++对象。到目前为止,在Me.CPP文件中都有这个工作(也把它推到Github),这个程序能够通过使用EMPTITY返回来存储所有给定的对象(我用PasePoBe后跟和PuthJuBeBead(new)来播放,但是EffisteBead我要工作,看看Me..CPP,它和这个代码有些相似).我不明白的是,为什么那些变量级别、描述和标签突然出现为空。在第一次迭代中,它们被写入,然后出现empty@tomzooi你不能忽视我解释的代码的问题。它的本质可能是有效的。一定要仔细阅读。在你的评论中:我可能稍后会看一看,但是很明显,你应该简化代码,让它只是演示问题。(有太多杂乱的输出,我不知道你真正的输出是什么意思。)。我查看了输出,但与我得到的结果没有什么不同。我将查看这些链接,我对迭代器不太熟悉,因此这可能是问题所在。我将看看是否可以说服Coliru展示在
void parse_json(int depth, boost::property_tree::ptree const& tree, Requirement& cur)
{
    cur.label       = tree.get("label",       "");
    cur.level       = tree.get("level",       "");
    cur.description = tree.get("description", "");

    if (auto kids = tree.get_child_optional("children")) {
        for (auto& kid : *kids) {
            std::cout << "at depth " << depth << "... " << std::flush;

            cur.children.emplace_back(&cur);

            std::cout << "going down" << std::endl;
            parse_json(depth + 1, kid.second, cur.children.back());
        }
    }
}
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <list>

class Requirement {
    public:
        bool empty;
        std::string level;
        std::string description;
        std::string label;
        Requirement const* parent;
        std::list <Requirement> children;

        Requirement(Requirement const* p);
        Requirement(std::string l, std::string d, std::string la, Requirement const* p); // unused

        void print(std::string indent = "");
        void print_json(std::ostream &os, std::string indent = "");
};

Requirement::Requirement(Requirement const* p) 
    : empty(false), parent(p)
{
}

Requirement::Requirement(std::string l, std::string d, std::string la,Requirement const* p) // unused
    : empty(false), 
      level(std::move(l)), description(std::move(d)), label(std::move(la)), parent(p)
{
}

void Requirement::print_json(std::ostream &os, std::string indent) {
    os  << "{";
    indent += '\t';

    os
         << "\n" << indent << "\"level\":\""       << level       << "\", "
         << "\n" << indent << "\"description\":\"" << description << "\"";

    if(label.length() > 1) {
        os << ",\n" << indent << "\"label\":\"" << label <<"\"";
    }

    if (!children.empty()) {
        os << ", \"children\":[\n";

        bool first = true;
        for(auto& child : children) {
            if (!first)
                os << ',';

            first=false;

            os << "\n" << indent;
            child.print_json(os, indent);
        }
        os << "]";
    }

    indent.resize(indent.size() - 1);
    os << "\n" << indent << "}";
}

void Requirement::print(std::string indent) {
    std::cout << indent << "level: " << level << " description:" << description << " label: " << label << std::endl;
    for (Requirement kid : children)
        kid.print(indent + '\t');
}

void parse_json(int depth, boost::property_tree::ptree const& tree, Requirement& cur)
{
    cur.label       = tree.get("label",       "");
    cur.level       = tree.get("level",       "");
    cur.description = tree.get("description", "");

    if (auto kids = tree.get_child_optional("children")) {
        for (auto& kid : *kids) {
            std::cout << "at depth " << depth << "... " << std::flush;

            cur.children.emplace_back(&cur);

            std::cout << "going down" << std::endl;
            parse_json(depth + 1, kid.second, cur.children.back());
        }
    }
}

int main(int argc, char* argv[])
{
    if (argc>1) try {
        std::ifstream ss(argv[1]);

        boost::property_tree::ptree pt;
        boost::property_tree::read_json(ss, pt);

        Requirement root(nullptr);
        parse_json(0, pt, root);

        std::cout << std::endl << std::endl;
        root.print("; debug: ");
        root.print_json(std::cout);
    }
    catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}
at depth 0... going down
at depth 0... going down
at depth 1... going down
at depth 2... going down
at depth 2... going down


; debug: level: should description:very well performance wise label: goperf
; debug:    level: should description:be listening to spaces as well label: lisspace
; debug:    level: will description:a lot of levels back down again label: 
; debug:        level: empty description:empty label: empty
; debug:            level: can description:skip all the way back here label: skiphere
; debug:            level: can description:take three linestr label: threelines
{
    "level":"should", 
    "description":"very well performance wise",
    "label":"goperf", "children":[

    {
        "level":"should", 
        "description":"be listening to spaces as well",
        "label":"lisspace"
    },
    {
        "level":"will", 
        "description":"a lot of levels back down again", "children":[

        {
            "level":"empty", 
            "description":"empty",
            "label":"empty", "children":[

            {
                "level":"can", 
                "description":"skip all the way back here",
                "label":"skiphere"
            },
            {
                "level":"can", 
                "description":"take three linestr",
                "label":"threelines"
            }]
        }]
    }]
}
#include <fstream>
#include <iostream>
#include "class.h" 
#include <vector>
#include <string>
//#include <regex>
#include <boost/regex.hpp>
#include <iterator>
#include <algorithm>
#include <math.h>

//how many spaces are interpreted as one tab or level
#define SPACES 8 
#define LINEBUFSIZE 500 

void parse_req(unsigned int cur_depth, std::ifstream &f, std::list<Requirement> &reqlist,unsigned int linenr=0) {
    std::string line;
    unsigned int count = 0;
    unsigned int depth = 0;
    Requirement * cur(nullptr);
    boost::regex re("\\t+|\\s{2,}"); //regex split parameter (one or more tabs or 2 or more spaces)

    std::list<Requirement>* lptr(&reqlist);
    while (std::getline(f, line))   {
        linenr++;
        //  std::cout << "(" << linenr << "): " << line; 
        depth = line.find_first_not_of("    "); //find first none space or tab character


        boost::sregex_token_iterator i(line.begin()+depth, line.end(), re, -1); //split line by tabs and spaces
        boost::sregex_token_iterator j;
        count = 0; //reset count
        if (depth == cur_depth) {
            lptr->emplace_back(lptr->back().parent);
            cur = &lptr->back();
        }
        else if (depth > cur_depth) { //go deeper
            cur_depth = depth;
            lptr = &lptr->back().children;
            lptr->emplace_back(cur);
            cur = &lptr->back();
        }
        else if (depth < cur_depth) { //go down
            //lptr = cur->parent;
            while (cur_depth > depth) { 
                cur_depth--;
                std::cout << "deeper : " << cur_depth << std::endl << std::flush;
                lptr = &(lptr->back().parent->parent->children);
            }
            if(cur_depth != 0) {
                lptr->emplace_back(lptr->back().parent);
                cur = &lptr->back();
            }
            else {
                reqlist.emplace_back(nullptr);
                cur = &reqlist.back();
            }
        }
        while(i != j) { //for all splitted parts
            switch(count) {
                case 0:
                    cur->level  =*i++;
                    //std::cout << "lvl: " << cur->level << " ";
                    break;
                case 1:
                    cur->description = *i++;
                    //std::cout << "dsc: " << cur.description << std::endl;
                    break;
                case 2:
                    cur->label = *i++;
                    break;
                default:
                    *i++;
                    break;
            }
            count++;
        }
        if (count < 2) { //too less arguments
            std::cout << "ERROR(" << linenr << "): nead at least two parts for an requirement (level and description), less than two found, halting." << std::endl;
            break;
        }
        if (count > 3) { //too much arguments
            std::cout << "WARNING(" << linenr << "): More then three arguments found, ignoring fourth or more argument" << std::endl;
        }
    }
}
int main(int argc, char *argv[]) {
//  class Requirement req("will",  "do good work", "good");
    if ( argc != 3 ) // argc should be 2 for correct execution
        // We print argv[0] assuming it is the program name
        std::cout<<"usage: "<< argv[0] <<" <filename input> <filename output>\n";
     else {
        std::ifstream file(argv[1]); //try to open file
        if(!file.is_open()) { //if we can't
            std::cout << "Could not open file" ;
        }
        else { //sucesfully opened file
            std::list<Requirement> requirements;
            parse_req(0,file,requirements);

            std::ofstream outfile(argv[2]); //try to open file
            outfile << "{ \"requirements\":[";
            for (Requirement req : requirements) {

                req.print();
                req.print_json(outfile);
                if (requirements.back().description != req.description) {
                    outfile << ",";
                }
            }
            outfile << "]}";
            std::cout << "success!" << std::endl;
            }

        }

    return 0;
}