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.
Related
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);
}
}
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();
}
I'm trying to obtain the last column of my CSV file. I tried using getline and the stringstream but it doesn't get the last column only
stringstream lineStream(line);
string bit;
while (getline(inputFile, line))
{
stringstream lineStream(line);
bit = "";
getline(lineStream, bit, ',');
getline(lineStream, bit, '\n');
getline(inputFile, line);
stringVector.push_back(bit);
}
My CSV file:
5.1,3.5,1.4,0.2,no
4.9,3.0,1.4,0.2,yes
4.7,3.2,1.3,0.2,no
4.6,3.1,1.5,0.2,yes
5.0,3.6,1.4,0.2,no
5.4,3.9,1.7,0.4,yes
Probably the simplest approach is to use std::string::rfind as follows:
while (std::getline(inputFile, line))
{
// Find position after last comma, extract the string following it and
// add to the vector. If no comma found and non-empty line, treat as
// special case and add that too.
std::string::size_type pos = line.rfind(',');
if (pos != std::string::npos)
stringVector.push_back(line.substr(pos + 1));
else if (!line.empty())
stringVector.push_back(line);
}
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();
How do I read in lines from a file and assign specific segments of that line to the information in structs? And how can I stop at a blank line, then continue again until end of file is reached?
Background: I am building a program that will take an input file, read in information, and use double hashing for that information to be put in the correct index of the hashtable.
Suppose I have the struct:
struct Data
{
string city;
string state;
string zipCode;
};
But the lines in the file are in the following format:
20
85086,Phoenix,Arizona
56065,Minneapolis,Minnesota
85281
56065
Sorry but I still cannot seem to figure this out. I am having a really hard time reading in the file. The first line is basically the size of the hash table to be constructed. The next blank line should be ignored. Then the next two lines are information that should go into the struct and be hashed into the hash table. Then another blank line should be ignored. And finally, the last two lines are input that need to be matched to see if they exist in the hash table or not. So in this case, 85281 is not found. While 56065 is found.
As the other two answers point out you have to use std::getline, but this is how I would do it:
if (std::getline(is, zipcode, ',') &&
std::getline(is, city, ',') &&
std::getline(is, state))
{
d.zipCode = std::stoi(zipcode);
}
The only real change I made is that I encased the extractions within an if statement so you can check if these reads succeeded. Moreover, in order for this to be done easily (you wouldn't want to type the above out for every Data object), you can put this inside a function.
You can overload the >> operator for the Data class like so:
std::istream& operator>>(std::istream& is, Data& d)
{
std::string zipcode;
if (std::getline(is, zipcode, ',') &&
std::getline(is, d.city, ',') &&
std::getline(is, d.state))
{
d.zipCode = std::stoi(zipcode);
}
return is;
}
Now it becomes as simple as doing:
Data d;
if (std::cin >> d)
{
std::cout << "Yes! It worked!";
}
You can use a getline function from <string> like this:
string str; // This will store your tokens
ifstream file("data.txt");
while (getline(file, str, ',') // You can have a different delimiter
{
// Process your data
}
You can also use stringstream:
stringstream ss(line); // Line is from your input data file
while (ss >> str) // str is to store your token
{
// Process your data here
}
It's just a hint. Hope it helps you.
All you need is function std::getline
For example
std::string s;
std::getline( YourFileStream, s, ',' );
To convert a string to int you can use function std::stoi
Or you can read a whole line and then use std::istringstream to extract each data with the same function std::getline. For example
Data d = {};
std::string line;
std::getline( YourFileStream, line );
std::istringstream is( line );
std::string zipCode;
std::getline( is, zipCode, ',' );
d.zipCode = std::stoi( zipCode );
std::getline( is, d.city, ',' );
std::getline( is, d.state, ',' );