0

I wrote a program that takes a file and reads it into a stringstream field in a class, and now I'm trying to interact with it. The problem is that when reading sequentially from several methods, one of the methods gives an error, or simply does not work. I guess the problem is how I read the file, how should I improve it?

There is my class:

class MatReader
{
protected:
    ...
    stringstream text;
    ...
    string PhysicsMaterial;
    string Diffuse;
    string NMap;
    string Specular;

public:
    /// <summary>
    /// Read all lines in .mat document by string
    /// </summary>
    void ReadAllLines(string file_path);
    /// <summary>
    /// Getting PhysicsMaterial property
    /// </summary>
    string getPhysMaterial();
    /// <summary>
    /// Getting diffuse file path
    /// </summary>
    string getDiffuseLocation();
};

And there is my implementation file:

#include "MaterialHandler.h"

void MatReader::ReadAllLines(string mat_file)
{
    ifstream infile(mat_file);
    string str;
    if (infile.is_open())
    {
        ofile = true;
        while (!infile.eof())
        {
            getline(infile, str);
            text << str+"\n";
        }
    }
    else
        throw exception("[ERROR] file does not exist or corrupted");
}

string MatReader::getPhysMaterial()
{
    string line;
    vector<string> seglist;
    try
    {
        if (ofile == false)
            throw exception("file not open");
    
        while (getline(text, line, '"'))
        {
            if (!line.find("/>"))
                break;
            seglist.push_back(line);
        }
        for (uint16_t i{}; i < seglist.size(); i++)
        {
            if (seglist[i-1] == " PhysicsMaterial=")
            {
                PhysicsMaterial = seglist[i];
                return seglist[i];
            }
        }
        line.clear();
        seglist.clear();
    }
    catch (const std::exception& ex)
    {
        cout << "[ERROR]: " << ex.what() << endl;
        return "[ERROR]";
    }
}

string MatReader::getDiffuseLocation()
{
    string line;
    vector<string> seglist;
    try
    {
        if (ofile == false)
            throw exception("file not open");
        while (getline(text, line, '"'))
        {
            seglist.push_back(line);
        }
        for (uint16_t i{}; i < seglist.size(); i++)
        {
            if (seglist[i - 1] == " File=")
            {
                PhysicsMaterial = seglist[i];
                return seglist[i];
            }
        }
    }
    catch (const std::exception& ex)
    {
        cout << "[ERROR]: " << ex.what() << endl;
        return "[ERROR]";
    }
}

The methods "getPhysMaterial()" and "getDiffuseLocation()" works separately without any problems, but if they are executed sequentially, they give an error or are not executed at all. Thank you.

Nikita
  • 11
  • 1
  • 1
    `seglist[i-1]` will access out of bounds on the first iteration (when `i` is 0). – 1201ProgramAlarm Dec 31 '20 at 14:32
  • 1
    As @1201ProgramAlarm pointed out, you're not handling the first iteration of the loop when the `i` is `0`. Also, it's helpful when you tell people what `error` you're receiving. – WBuck Dec 31 '20 at 14:49
  • 1
    Also, you're not resetting the internal `std::stringstream` pointer (it keeps track of how much you have consumed). That's why it doesn't work when you call each method sequentially. – WBuck Dec 31 '20 at 14:53
  • @1201ProgramAlarm yeah, much appreciated i will figure out with it, but that doesn't solve the problem. If I just remove the array output - it still doesn't work, I asked about the problem with the string stream, not the array output. – Nikita Dec 31 '20 at 15:00
  • @WBuck Oh, okay... Thanks, and how can i reset stringstream pointer? seekp(0) doesn't work for me. – Nikita Dec 31 '20 at 15:03
  • You'll need to use `seekg` not `seekp`. `seekp` sets the **output** indicator. You want to set the **input** indicator (via `seekg`) – WBuck Dec 31 '20 at 15:23
  • Your question should come with a [mcve], which also helps solving problems yourself. As a new user, please also take the [tour] and read [ask]. – Ulrich Eckhardt Dec 31 '20 at 16:06

1 Answers1

0

So first you need to correct the issue with your out-of-range array access. The next issue you have is you need to reset the position of the stream in order to re-read it from the beginning.

Here is an example of how you can do that.

std::stringstream ss{ };
ss << "This is line one\n"
   << "This is line two\n"
   << "This is line three\n"
   << "This is line four\n";

// Points to the start of the stringstream.
// You can store this as a member of your class.
const std::stringstream::pos_type start{ ss.tellg( ) };

std::string line{ };
while ( std::getline( ss, line ) )
    std::cout << line << '\n'; 

// Reset to the start of the stringstream.
ss.clear( );
ss.seekg( start );

while ( std::getline( ss, line ) )
    std::cout << line << '\n'; 

Another issue I noticed is that you're checking for eof in the loop condition. Don't do that.. Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?

Do something like this instead:

std::stringstream ss{ };
if ( std::ifstream file{ "/Path/To/MyFile.txt" } )
{
    std::string input{ };
    while ( std::getline( file, input ) )
        ss << input << '\n';
}
else 
{
    std::cerr << "Failed to open file\n";
    return 1;
}

std::string line{ };
while ( std::getline( ss, line ) )
    std::cout << line << '\n'; 
WBuck
  • 5,162
  • 2
  • 25
  • 36