Stringstream not extracting after clearing input buffer - c++

I'm having trouble with stringstream. So I am getting tokens from a csv file and assigning them to some variables but I am clearing stringstream sv after I read every token and after the very first token it stops working and I am not sure why. The commented line, 'stops working here', is where it stops correctly extracting. In visual studio it says sv is '0x000' even after insert operation. I even have another loop in my code that clears it once and does insertion again and that works.
int reviewid;
int userid;
int rating;
string reviewDate;
getline(reviewReader, dummyLine); // skip first line of input
while (getline(reviewReader, input)) {
stringstream ss, sv; // sv used for type conversions
// using delimeter of commas
// order of input in the file is [ movieid, moviename, pubyear]
// , first toiken is movieid, second token will be moviename
// third token will be pubyear
ss << input; // take in line of input
getline(ss, token, ','); // first token
sv << token; // convert toiken to int
sv >> reviewid;
getline(ss, token, ','); //
sv.str("");
sv << token; // STOPS WORKING HERE
sv >> movieid;
sv.str(""); // clear buffer
getline(ss, token, ',');
sv << token;
sv >> userid;
sv.str("");
getline(ss, token, ',');
sv << token;
sv >> rating;
sv.str("");
getline(ss, token, ',');
sv << token;
sv >> reviewDate;
sv.str("");
Review r = Review(reviewid, movieid, userid, rating, reviewDate); // make review object
reviews.push_back(r); // add it to vector of reviews
}

str("") does not change the state of your stream.
i.e. if the stream was in eof state before str("") it will still be in the same state after str("").
In order to clear the state please use clear();
sv.str("");
sv.clear();

Related

I keep gettin an stoi conversion error when trying to open a file

So im trying to read a file's contents but everything I run the program I get an error stating
libc++abi: terminating with uncaught exception of type std::invalid_argument: stoi: no conversion
I cant seem to figure out why it's giving me that error. When I input the file name and hit enter I get this error.
void displayGrades(string fileInput, vector<GradeItem> &content) {
ifstream myFile(fileInput);
string date, description, type, tempG, tempMG, line;
int grade, maxGrade;
std::getline(myFile, line);
while(std::getline(myFile, line)){
std::istringstream iss(line);
std::getline(iss, date);
std::getline(iss, description);
std::getline(iss, type);
std::getline(iss, tempG);
grade = stoi(tempG);
std::getline(iss, tempMG);
maxGrade = stoi(tempMG);
GradeItem G(date, description, type, grade, maxGrade);
content.push_back(G);
}
}
File contents:
Date, Description, Type, Grade, MaxGrade
04/13/2022,Today is a good day,quiz,100,1
Any help would be nice!
std::getline when not provided by a delimiter reads the whole line of the input stream.
So, what's happening here is that you are reading the whole line in the file into the date variable, while the other strings remain empty.
To correct this, you can add a third argument to the std::getline function which is a delimitation character that tells the function to stop reading further input after reaching this character(here ',').
Here is an example:
std::getline(iss, date, ',');
Your modified code should be:
void displayGrades(string fileInput, vector<GradeItem> &content) {
ifstream myFile(fileInput);
string date, description, type, tempG, tempMG, line;
int grade, maxGrade;
std::getline(myFile, line);
while(std::getline(myFile, line)){
std::istringstream iss(line);
std::getline(iss, date, ',');
std::getline(iss, description, ',');
std::getline(iss, type, ',');
std::getline(iss, tempG, ',');
grade = stoi(tempG);
std::getline(iss, tempMG);
maxGrade = stoi(tempMG);
GradeItem G(date, description, type, grade, maxGrade);
content.push_back(G);
}
}

How do I read an input file line by line into multiple arrays of different types

As an example input file:
02-03-2004,13045634
03-02-2004,16782930
I'm having trouble coming up with code that can read in inputs into different arrays properly. My most recent attempt is this:
int i = 0;
if(inputFile.is_open()){
while(!inputFile.eof() && i < arraySize){
getline(inputFile, line, ',');
date[i] = line; //array of type string
getline(inputFile, line, ',');
deaths[i] = line; //array of type int
//...
i++;
}
}
I'm struggling to figure out how exactly I'm supposed to move through the input file with the delimiter of ',' while storing everything correctly.
The array I'm trying to read into is a dynamic array, where I already have a function made that determines the size of the array through getline
I also have to keep the ints as ints because there are functions that need to do calculations with them
As a side note, I can't use vectors for this because they haven't been covered in my class yet
You can do this easily enough, assuming your input is invariably two items on a line, comma-separated:
std::vector<std::string> dates;
std::vector<std::string> IDs;
std::ifstream f( ... );
std::string date, ID;
while (getline( f, date, ',' ) and getline( f, ID ))
{
dates.push_back( date );
IDs.push_back( ID );
}
I also wholly recommend Some programmer dude’s comments above: get each line of input and then use a std::istringstream to parse it, and use a struct for your data:
struct Ticket
{
std::string date;
std::string job_number;
};
std::vector<Ticket> tickets;
And so on.
Ok, I got it working now. It seems like the problem wasn't the way I was getting the input, but rather the fact that the loop wouldn't run. Turns out that I had to close the file when I was done using it in a different function and then reopen it when it came to the input reading function to reset the while loop's condition.
I got the input working properly with this code after I fixed the loop issue:
void getArrayData(ifstream &inputFile, string *&date, int *&death, string *& state, int *&cas, int arrSize, int *&fip) {
string temp;
int k = 0;
while(getline(inputFile, temp)){
string workingLine;
stringstream ss(temp);
getline(ss, date[k], ',');
getline(ss, state[k], ',');
getline(ss, workingLine, ',');
fip[k] = stoi(workingLine);
getline(ss, workingLine, ',');
cas[k] = stoi(workingLine);
getline(ss, workingLine, ',');
death[k] = stoi(workingLine);
k++;
}
inputFile.close();
}

c++ read in multiple lines, varied delimiters

The input file is structured like:
First Last,33,Male,city
score1,15/30
score2, 20/20
First Last,43,Female,city
score1,20/20
score2,18/20
with an unknown number of records, each separated by a blank line. Each record becomes an object to be stored in a dynamic array object (which is confusing in its own right).
I can place the first line into variables, but the remaining lines are lost in the ether. Although it produces the proper number of records (in this case, it would be 2), each record is filled with the first line's data and no scores.
I just have no ideas about how to read it in properly, and still don't know how to deal with that blank line in between each record. This was for a previous homework for which I cannot get a straight answer out of anybody, and would love to know, since reading in from files seems to be all the rage...
Here is what I have:
std::ifstream read_data(data_file);
std::string line;
while(std::getline(read_data, line))
{
std::stringstream ss(line);
char detectNewline;
getline(ss, tempName, ',');
getline(ss, tempAgeString, ',');
tempAge = std::atoi(tempAgeString.c_str());
getline(ss, tempGender, ',');
getline(ss, tempCity, '\n';
for(int i=0; i < 2; i++) // I am not married to this idea, seems efficient
{
getline(ss, tempScore, ',');
getline(ss, pointsEarnedHolder, '/');
tempPointsEarned += std::atof(pointsEarnedHolder.c_str());
getline(ss, totalPointsHolder, '\n');
tempTotalPoints += std::atof(totalPointsHolder.c_str());
}
// variable manipulation
ClassName object(proper vars);
previouslyDeclaredDynamicArrayObject(object);
detectNewline = read_data.peek();
if(detectNewline == '\n')
{
std::cin.ignore();
}
} //while
Thank you for any insight!
I will touch on a way to read the information efficiently.
First you can getline the first line and parse the information like you have. Then you will parse the information provided in the scores until getline hits a blank line. Then once this happens you will add the object into the array, and get the starting information for the next object and then repeat the process.
The code would look similar to this (pretty pseudo-y):
std::getline(read_data, line);
while( !read_data.eof() ) {
std::stringstream ss(line);
getline(ss, tempName, ',');
getline(ss, tempAgeString, ',');
tempAge = std::atoi(tempAgeString.c_str());
getline(ss, tempGender, ',');
getline(ss, tempCity, '\n';
std::getline( read_data, line )
while( line != "\n" ) {
getline(ss, tempScore, ',');
getline(ss, pointsEarnedHolder, '/');
tempPointsEarned += std::atof(pointsEarnedHolder.c_str());
getline(ss, totalPointsHolder, '\n');
tempTotalPoints += std::atof(totalPointsHolder.c_str());
std::getline( read_data, line )
}
// variable manipulation
ClassName object(proper vars);
previouslyDeclaredDynamicArrayObject(object);
std::getline(read_data, line);
} //while
This is assuming that you are properly extracting the information from the lines.
An easier way to delimit upon those characters is to classify them as whitespace through the std::ctype<> facet of the streams locale. Then you can simply use the extractor operator>>() instead of parsing through the unformatted functions.
Here's an example of how to set the facet:
struct ctype : std::ctype<char>
{
static mask* make_table()
{
const mask* table = classic_table();
static std::vector<mask> v(table, table + table_size);
v[' '] |= space;
v[','] |= space;
v['/'] |= space;
return &v[0];
}
ctype() : std::ctype<char>(make_table()) { }
};
You can then make a convience function to install it into the stream's locale:
std::istream& custom_ctype(std::istream& is)
{
is.imbue(std::locale(is.getloc(), new ctype));
return *is;
}
// ...
file >> custom_ctype;
After that it becomes simple to extract characters from the file into variables. Just think of the characters that you want to ignore as if they were the space character or the newline character, because that's exactly what we've done here.

Possible vacancy compensation for sscanf

I have a string that is written as follows:
^SYSINFO:2,3,0,3,1,,3
You will notice that there is one digit missing in the line, this may not always be the case. I use sscanf to scan the line and extract the last integer.
sscanf(response_c, "^SYSINFO:%*d,%*d,%*d,%*d,%*d,,%d", &networkattach_i);
How can I compensate for the digit that is currently missing, but may be written also?
The following does NOT work:
sscanf(response_c, "^SYSINFO:%*d,%*d,%*d,%*d,%*d,%*d,%d", &networkattach_i);
You can try using getline to parse your string. An example would be,
using namespace std;
string input = "^SYSINFO:2,3,0,3,1,,3";
istringstream ss(input);
string token;
getline(ss, token, ':'); //reads till SYSINFO
while(getline(ss, token, ',')) {
cout << token << '\n';
}

How do I access individual words after splitting a string?

std::string token, line("This is a sentence.");
std::istringstream iss(line);
getline(iss, token, ' ');
std::cout << token[0] << "\n";
This is printing individual letters. How do I get the complete words?
Updated to add:
I need to access them as words for doing something like this...
if (word[0] == "something")
do_this();
else
do_that();
std::string token, line("This is a sentence.");
std::istringstream iss(line);
getline(iss, token, ' ');
std::cout << token << "\n";
To store all the tokens:
std::vector<std::string> tokens;
while (getline(iss, token, ' '))
tokens.push_back(token);
or just:
std::vector<std::string> tokens;
while (iss >> token)
tokens.push_back(token);
Now tokens[i] is the ith token.
You would first have to define what makes a word.
If it's whitespace, iss >> token is the default option:
std::string line("This is a sentence.");
std::istringstream iss(line);
std::vector<std::string> words.
std::string token;
while(iss >> token)
words.push_back(token);
This should find
This
is
a
sentence.
as words.
If it's anything more complicated than whitespace, you will have to write your own lexer.
Your token variable is a String, not an array of strings. By using the [0], you're asking for the first character of token, not the String itself.
Just print token, and do the getline again.
You've defined token to be a std::string, which uses the index operator [] to return an individual character. In order to output the entire string, avoid using the index operator.