KSchank
KSchank

Reputation: 127

C++ std::stack empties after each iteration of while loop

I'm trying to write a function used to parse XML tags for XML like this:

<Employee>
    <salary>40000</salary> <name>John Doe</name> <id>1234</id>
    <address>2230 W. Treeline Dr.</address>
    <city>Tucson</city>
    <state>Arizona</state>
    <country>USA</country>
    <phone>520-742-2448</phone>
</Employee> <Employee>
    <salary>60000</salary> <name>Jane Doe</name> <id>4321</id> </Employee>
<Employee>
    <salary>140000</salary>
    <state>Michigan</state>
    <name>Jack Dough</name>
    <id>12345</id>
    <city>Dearborn</city>
    <country>USA</country>
    <phone>303-427-0153</phone>
    <address>24437 Princeton</address>
</Employee>

I'm using an std::stack to keep track of the tags. When an opening tag (e.g. <Employee>) is encountered, a string representation of the tag name (Employee) is pushed onto the stack, and, assuming the XML is written correctly, encountering the matching closing tag (e.g. </Employee>) causes the tag name to be popped off of the stack. However, I have run into an issue where a string is pushed onto the stack so the stack has a size of 1. When the body of the while loop is reached and the body is executed a second time, the stack is emptied. I can't for the life of me figure out what I'm doing wrong. Why does my stack "reset" every time the while loop reiterates?

static void fromXML(std::istream& in_stream) {
    std::stack<std::string> tags;
    std::string temp;
    std::string tagName;
    std::getline(in_stream, temp);
    std::istringstream ss(temp);

    bool building_employee = true;
    int _id;
    std::string _name;
    std::string _address = "";
    std::string _city = "";
    std::string _state = "";
    std::string _country = "";
    std::string _phone = "";
    double _salary = 0.0;        

    while(building_employee && std::getline(ss, temp, '<')) {
        std::cout << "Size: " << tags.size() << std::endl; // Prints "0"


        std::getline(ss, tagName, '>');
        ss.ignore(256, '<');

        if (tagName == "") { }
        else if ( !tags.empty() && tagName == "/" + tags.top() ) {
            std::cout << "Popping " << tags.top() << std::endl;
            tags.pop();
        }
        else if ((tags.empty() && tagName == "Employee") || (!tags.empty() && tagName != "Employee")) {
            std::cout << "Pushing " << tagName << std::endl;
            tags.push(tagName);                
        }
        else {                 
            std::cerr << "Problem with XML." << std::endl;
        }

        if (tagName == "/Employee") { building_employee = false; }
        else if (tagName == "id") { _id = std::stoi(temp); }
        else if (tagName == "name") { _name = temp; }


        if (!building_employee) { std::cout << "Employee end." << std::endl; }
        if(in_stream.eof()) {
            std::cout << "Error: End of file reached without closing <" << temp << ">" << std::endl;
        }
        std::cout << "Size: " << tags.size() << std::endl; // Prints "1" after pushing one string onto the stack
    }
    if (temp == "/Employee") { std::cout << "Employee object succesfully created" << std::endl; }
}

Upvotes: 0

Views: 98

Answers (1)

3CxEZiVlQ
3CxEZiVlQ

Reputation: 38425

Let me be polite for other and do Minimal, Complete, and Verifiable example for them.

#include <iostream>
#include <sstream>
#include <limits>
#include <stack>

static void fromXML(std::istream& in_stream) {
    std::stack<std::string> tags;
    std::string temp;
    std::string tagName;
    std::getline(in_stream, temp);
    std::istringstream ss(temp);

    bool building_employee = true;
    int _id;
    std::string _name;
    std::string _address = "";
    std::string _city = "";
    std::string _state = "";
    std::string _country = "";
    std::string _phone = "";
    double _salary = 0.0;        

    while(building_employee && std::getline(ss, temp, '<')) {
        std::cout << "Size 1: " << tags.size() << std::endl; // Prints "0"


        std::getline(ss, tagName, '>');
        ss.ignore(256, '<');

        if (tagName == "") { }
        else if ( !tags.empty() && tagName == "/" + tags.top() ) {
            std::cout << "Popping " << tags.top() << std::endl;
            tags.pop();
        }
        else if ((tags.empty() && tagName == "Employee") || (!tags.empty() && tagName != "Employee")) {
            std::cout << "Pushing " << tagName << std::endl;
            tags.push(tagName);                
        }
        else {                 
            std::cerr << "Problem with XML." << std::endl;
        }

        if (tagName == "/Employee") { building_employee = false; }
        else if (tagName == "id") { _id = std::stoi(temp); }
        else if (tagName == "name") { _name = temp; }


        if (!building_employee) { std::cout << "Employee end." << std::endl; }
        if(in_stream.eof()) {
            std::cout << "Error: End of file reached without closing <" << temp << ">" << std::endl;
        }
        std::cout << "Size 2: " << tags.size() << std::endl; // Prints "1" after pushing one string onto the stack
    }
    if (temp == "/Employee") { std::cout << "Employee object succesfully created" << std::endl; }
}

int main()
{
    std::istringstream input(
        "<Employee>\n"
        "    <salary>40000</salary> <name>John Doe</name><id>1234</id>\n"
        "    <address>2230 W. Treeline Dr.</address>\n"
        "    <city>Tucson</city>\n"
        "    <state>Arizona</state>\n"
        "    <country>USA</country>\n"
        "    <phone>520-742-2448</phone>\n"
        "</Employee> <Employee>\n"
        "    <salary>60000</salary> <name>Jane Doe</name> <id>4321</id> </Employee>\n"
        "<Employee>\n"
        "    <salary>140000</salary>\n"
        "    <state>Michigan</state>\n"
        "    <name>Jack Dough</name>\n"
        "    <id>12345</id\n"
        "    <city>Dearborn</city>\n"
        "    <country>USA</country>\n"
        "    <phone>303-427-0153</phone>\n"
        "    <address>24437 Princeton</address>\n"
        "</Employee>)\n");
    fromXML(input);
}

Why does my stack "reset" every time the while loop reiterates?

The stack does not reset, the loop does not iterate. Follow @JiveDadson advice and use a debugger.

The key of the issue consists of two lines

std::getline(in_stream, temp);
std::istringstream ss(temp);

You read one line there and your loop operates on this line only. Since it is the only line, the loop's body is executed once only. Remove those lines and change all corresponding occurrences of ss in the code to in_stream.

while(building_employee && std::getline(in_stream, temp, '<')) {
    // ...
    std::getline(in_stream, tagName, '>');
    in_stream.ignore(256, '<');

Upvotes: 1

Related Questions