0

I am trying to make a program that opens a list of XML files and recovers some data from them so I decided to use RapidXML.

Everything is fine for the first document but after I try to open another file in a loop the program crashes because RapidXML function finds a null pointer to the next child.

I have tried placing the RapidXML declarations in a global scope so that they don't get destroyed but the problem still persists. What can I do?

Here's the code:

#include <filesystem>
#include <string>
#include <fstream>
#include <streambuf>
#include <sys/stat.h>
#include "rapidxml.hpp"
#include <iostream>
using namespace rapidxml;
using namespace std;
namespace fs = std::filesystem;

xml_document<> doc;
xml_node<>* node;
xml_node<>* child;

int main()
{
    int counter = 0;
    std::string path;
    struct stat sb;

    // Fase di ricezione del percorso dei file da leggere in XML
    cout << "Inserire il percorso dove sono presenti i file da leggere:\n";
    do {
        if (counter > 0) cout << "Attenzione, percorso non valido!\n";
        cin >> path;
        counter++;
    } while (stat(path.c_str(), &sb));
    cout << "Percorso valido.\n";

    // Fase di lettura della lista dei file presenti nella cartella sopra selezionata
    for (const auto& entry : fs::directory_iterator(path)) 
    {
        if (entry.path().extension().string().ends_with(".xml")) 
        {
            if (entry.path().string().ends_with(".xml_metaDato.xml")) continue;
            // Fase di apertura e impostazione del percorso
            else {
                cout << entry.path() << std::endl;

                std::ifstream t(entry.path().filename());
                std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
                str.append("\0");

                doc.parse<0>(&str[0]);         // 0 means default parse flags

                // if (doc.first_node() == nullptr) cout << "Trying to reference a null pointer";
                // cout << doc.first_node()->name() << "\n";

                node = doc.first_node();
                for (child = node->first_node(); child; child = child->next_sibling())
                {
                    cout << child->name() << std::endl;
                }

                doc.clear();
                t.close();
            }
        }
    }
}

The problem is here, at the last part of the most inner if:

        node = doc.first_node();
        for (child = node->first_node(); child; child = child->next_sibling())
        {
            cout << child->name() << std::endl;
        }

After opening a new file it tries to read data from it but it crashes for a null pointer.

  • Taking a wild guess, don't use those global variables. In particular move `doc` so that it is recreated for each parsed document. Just a guess, but it is a simple change and would be the first thing I would try. – john Mar 24 '23 at 11:26
  • Also `str.append("\0");` seems to be a mistake as it will not append anything, I'm sure you meant `str.append(1, '\0');` to append a null character to your string (`str += '\0';` would also work). – john Mar 24 '23 at 11:28
  • @john Where should I put `doc`? Inside the main function? – randomizzatore Mar 24 '23 at 11:34
  • Put it right where you need it `xml_document<> doc; doc.parse<0>(&str[0]);` But definitely inside the for loop, that's the point. But as I said earlier, just guessing. – john Mar 24 '23 at 14:04
  • @john I have tried but it still gives the same problem. I am thinking I am missing some manual procedure for RapidXML, otherwise I don't know what's happening... – randomizzatore Mar 24 '23 at 14:14
  • I think it's a simple error `std::ifstream t(entry.path().filename());` should be `std::ifstream t(entry.path());`. The method `filename()` only returns the file name not the full path. So your file does not open. – john Mar 24 '23 at 14:40

1 Answers1

1

The error has nothing to to do with rapidxml. The line of code

std::ifstream t(entry.path().filename());

is incorrect since filename() only returns the file name portion of the complete path, so given "C:/fred/john.xml" it would return "john.xml". This means any file outside of the current working directory will fail to open.

The correct code is simply

std::ifstream t(entry.path());

although on older versions of C++ you might need

std::ifstream t(entry.path().c_str());
john
  • 85,011
  • 4
  • 57
  • 81