Input file reading wrong value and eof controlled loop does not end - c++

I'm attempting to read data from an input file using a loop that should stop at the end of the file within a function. The data in the file is arranged in lines like this:
Zip Drive
89
Warehouse C
75.89
Keyboard
133
Warehouse C
25.95
On the fourth line the variable is filled with some garbage input and everything read after is blank. The loop also will not end.
int ReadData(string productName[], string productLocation[], int productQty[], double productPrice[])
{
ifstream infile;
int i = 0;
infile.open("prog1.txt");
do {
getline(infile, productName[i]);
infile >> productQty[i];
getline(infile, productLocation[i]);
infile >> productPrice[i];
i++;
} while (!infile.eof());
infile.close();
return i;
}

The infile >> productQty[i] and infile >> productPrice[i] statements are reading int/double values and leaving the line breaks after them in the stream, to be read by the next std::getline() call, which is not what you want to happen.
You need to either:
use std::getline() to read every line, and then use std::istringstream to parse the int/double values from their respective lines:
int ReadData(string productName[], string productLocation[], int productQty[], double productPrice[])
{
ifstream infile("prog1.txt");
int i = 0;
while (getline(infile, productName[i]))
{
getline(infile, line);
istringstream(line) >> productQty[i];
getline(infile, productLocation[i]);
getline(infile, line);
istringstream(line) >> productPrice[i];
++i;
}
infile.close();
return i;
}
call infile.ignore() after reading the int/double values, to read and dismiss the line breaks:
int ReadData(string productName[], string productLocation[], int productQty[], double productPrice[])
{
ifstream infile("prog1.txt");
int i = 0;
while (getline(infile, productName[i]))
{
infile >> productQty[i];
infile.ignore(numeric_limits<streamsize>::max(), '\n');
getline(infile, productLocation[i]);
infile >> productPrice[i];
infile.ignore(numeric_limits<streamsize>::max(), '\n');
++i;
}
infile.close();
return i;
}
In either case, the arrays must be pre-allocated with enough elements to hold as many products as are in the file. Since the code has no way of actually verifying that count, I would suggest an alternative safer approach:
struct productInfo
{
string name;
string location;
int quantity;
double price;
};
istream& operator>>(istream &in, productInfo &out)
{
string line;
if (!getline(in, out.name)) {
// don't set failbit yet, in case this is just EOF...
return in;
}
// at this point, a new product is encountered, so
// any missing data is considered a failure...
if (!getline(in, line)) {
in.setstate(failbit);
return in;
}
if (!(istringstream(line) >> out.quantity)) {
in.setstate(failbit);
return in;
}
if (!getline(in, out.location)) {
in.setstate(failbit);
return in;
}
if (!getline(in, line)) {
in.setstate(failbit);
return in;
}
if (!(istringstream(line) >> out.price)) {
in.setstate(failbit);
return in;
}
return in;
}
int ReadData(vector<productInfo> &products)
{
ifstream infile("prog1.txt");
int i = 0;
productInfo info;
while (infile >> info)
{
products.push_back(info);
++i;
}
infile.close();
return i;
}

Related

Read different data types from .csv file and store them into a struct array

I need to read a .csv file, put the information into a struct, then insert the struct into a binary file. Each column means:
(int)Year; (int)Rank; (char*)University's Name; (float)Score; (char*)City; (char*)Country
My .csv file looks like:
2018,1,Harvard University,97.7,Cambridge,United States
2018,2,University of Cambridge,94.6,Cambridge,United Kingdom
2018,3,University of Oxford,94.6,Oxford,United Kingdom
2018,4,Massachusetts Institute of Technology (MIT),92.5,Cambridge,United States
2018,5,Johns Hopkins University,92.1,Baltimore,United States
As you can see, it contains the five best universities in the world.
The issue is my code can read neither the integers (perhaps due to commas in the file) nor the char[30].
struct Data
{
int year;
int rank;
char name[30];
float score;
char city[30];
char country[30];
};
`Data *universitie = new Data [5];
ifstream read ( "myFile.csv" );
int i = 0;
while ( !read.eof() ) {
read >> universitie[i].year;
read >> universitie[i].rank;
read.getline (universitie[i].name, 30, ','); //here a segmentation fault happened
read >> universitie[i].score;
read.getline (universitie[i].city, 30, ','); //here a segmentation fault happened
read.getline (universitie[i].country, 30, '\n'); //here a segmentation fault happened
i++;
}
read.close ();
I'm actually using char[30] for name, city and country instead of string because then I'll write this struct array into a binary file.
How can I properly read the integers until the comma?
How to read a character array from a file using getline() with delimeters like ,?
This is for my Computer Science course's task.
The usual technique with CSV files is to model a record with a class, then overload operator>> to input a record:
struct Record
{
int year;
int rank;
std::string name;
double score;
std::string city;
std::string country;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
char comma;
input >> r.year;
input >> comma;
input >> r.rank;
input >> comma;
std::getline(input, r.name, ',');
input >> r.score;
std::getline(input, r.city, ',');
std::getline(input, r.country, '\n');
return input;
}
A use case for the above could look like:
std::vector<Record> database;
ifstream data_file ( "myFile.csv" );
Record r;
while (data_file >> r)
{
database.push_back(r);
}
Note: I have changed your char[] to std::string for easier handling. Also, I changed the while loop condition.
//Open the file
Data *universitie = new Data [5];
int i = 0;
std::string line;
while(std::getline(file, line))
{
std::stringstream ss;
ss << line;
ss >> universitie[i].year;
ss.ignore();
ss >> universitie[i].rank;
ss.ignore();
ss >> universitie[i].name;
ss.ignore();
ss >> universitie[i].score;
ss.ignore();
ss >> universitie[i].city;
ss.ignore();
ss >> universitie[i].country;
i++;
}
This is a solution with std::stringstream. The ignore() function is used to skip the ',' between the entries.
Also in my opinion is better to use the C++ std::string class instead of char[30]. If at some point you need c string then you can use the c_str() function.

Read text file and store student info into a a vector [duplicate]

This question already has an answer here:
Read lines from text file and store into array
(1 answer)
Closed 1 year ago.
the text file looks something like this:
9528961 Adney Smith CS 4.2
9420104 Annalynn Jones EE 2.6
9650459 Bernadette Williams IT 3.6
...
there are 45 lines in the text file meaning 45 students. I have read the text file and when I run the program I get this:
9428167
Mason
Taylor
CS
4.8
9231599
Alexander
Jones
CS
2.3
My main file looks like this:
int main()
{
auto student = new Student<string>();
std::vector<string> students;
std::ifstream inputFile;
inputFile.open("enroll_assg.txt");
std::string line;
if(inputFile.is_open()){
while(std::getline(inputFile, line)){
std::istringstream iss(line);
std::string word;
while(iss >> word){
std::cout << word << std::endl;
for(int i = 0; i < 5; i++){
}
}
}
}
return 0;
}
Each student has 5 columns (id, fname, lname, department, gpa) and I need make a vector which includes all these student object. I need some help doing this so comments and answers are most welcome. Thank you.
IMHO, the best method is to use a struct or class to model or represent the data record you need to read.
struct Student
{
unsigned int id;
std::string first_name;
std::string last_name;
std::string major_code;
double gpa;
friend std::istream& operator>>(std::istream& input, Student& s);
};
std::istream& operator>>(std::istream& input, Student& s)
{
input >> s.id;
input >> s.first_name;
input >> s.last_name;
input >> s.major_code;
input >> s.gpa;
input.ignore(10000, '\n'); // Synchronize to next line.
return input;
}
Your input code could look like this:
std::vector<Student> database;
Student s;
//... open file.
while (student_file >> s)
{
database.push_back(s);
}
The above code will read each student record into a database, so you can analyze it.
Try something more like this instead:
int main()
{
std::ifstream inputFile("enroll_assg.txt");
if (inputFile.is_open()){
std::vector<Student<string>> students;
std::string line;
while (std::getline(inputFile, line)){
std::istringstream iss(line);
Student<string> student;
iss >> student.id;
iss >> student.fname;
iss >> student.lname;
iss >> student.department;
iss >> student.gpa;
students.push_back(student);
}
// use students as needed...
}
return 0;
}
Then, you should consider having Student overload the operator>>, which will greatly simplify the loop so you can do something more like this instead:
template<typename T>
std::ostream& operator>>(std::ostream &in, Student<T> &student)
{
std::string line;
if (std::getline(in, line))
{
std::istringstream iss(line);
iss >> student.id;
iss >> student.fname;
iss >> student.lname;
iss >> student.department;
iss >> student.gpa;
}
return in;
}
int main()
{
std::ifstream inputFile("enroll_assg.txt");
if (inputFile.is_open()){
std::vector<Student<string>> students;
Student<string> student;
while (inputFile >> student){
students.push_back(student);
}
// use students as needed...
}
return 0;
}
First this question is a duplicate of Read lines from text file and store into array which already has the answer you're looking for in this question.
The below shown program uses struct to represent a given Student and it also used a std::vector. You can use this program as a reference(starting point). It reads student information from the input text file and store that information in a vector of Student.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
//this class represents a Student
class Student
{
public:
std::string firstName, lastName, courseName ;
unsigned long id = 0;
float marks = 0;
};
int main()
{
std::ifstream inputFile("input.txt");
std::string line;
std::vector<Student> myVec;//create a vector of Student objects
if(inputFile)
{
while(std::getline(inputFile, line))
{
Student studentObject;
std::istringstream ss(line);
//read the id
ss >> studentObject.id;
//read the firstname
ss >> studentObject.firstName;
//read the lastname
ss >> studentObject.lastName;
//read the courseName
ss >> studentObject.courseName;
//read the marks
ss >> studentObject.marks;
if(ss)//check if input succeded
{
myVec.emplace_back(studentObject);//add the studentObject into the vector
}
}
}
else
{
std::cout<<"File cannot be opened"<<std::endl;
}
//lets print out the elements of the vecotr to confirm that all the students were correctly read
for(const Student &elem: myVec)
{
std::cout << elem.id << ": "<<elem.firstName<<" "<<elem.lastName<<" "<<elem.courseName<<" "<<elem.marks <<std::endl;
}
return 0;
}
The output of the above program can be seen here.

C++ Reading text file into struct data members

I'm currently trying to load a text file into struct data members. Each number is separated by a comma.
#include<string>
#include<sstream>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
};
int main(){
string fileName;
string line;
pass Pass;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName);
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
}
Here is an example of the text file.
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
3.425838997,1.603712153,0
3.502553324,0.998192867,0
3.917348666,1.49223429,0
4.391605986,0.831661367,0
4.947059678,0.8557003,0
5.429305232,2.42029408,0
The data in the text file follows this format:
arrivalTime,serviceTime,classType
As you can see i have split the line up and stored it in "line" using the comma delimiter, but i am unsure how to load each number into the struct in the while loop.
Any help would be appreciated.
Define an istream operator >> for your struct. Something like
struct pass {
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
friend std::istream & operator >>(std::istream & in, pass & p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
Then, simply
pass Pass;
fin >> Pass;
while (getline(fin, line))
{
sscanf(line.c_str(), "%lf,%lf,%d", &arrivalTime, &serviceTime, &classType);
}
This loop is wrong:
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
You are reading everything from the stream up to the next ',' character, then trying to read more from the stream.
Given the input file:
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
Your program reads "0.951412936" into line (and discards the ',') then tries to read the next input into Pass[0].arrivalTime but the next input is 2.131445423, which was meant to be the serviceTime (which you already read into line).
As Shreevardhan suggests you can define an operator for reading your struct from a stream. I would make it more reliable like so:
struct ExpectedChar { char expected; };
// read a character from a stream and check it has the expected value
std::istream& operator>>(std::istream& in, const ExpectedChar& e)
{
char c;
if (in >> c)
if (c != e.expected) // failed to read expected character
in.setstate(std::ios::failbit);
return in;
}
// read a struct pass from a stream
std::istream& operator>>(std::istream& in, pass& p)
{
ExpectedChar comma{ ',' };
in >> p.arrivalTime >> comma >> p.serviceTime >> comma >> p.classType;
return in;
}
This will stop reading if the input file does not meet the expected format. Now you can do:
while (fin >> Pass)
{
// do something with each pass
}
if (!fin.eof()) // stopped reading before end-of-file
throw std::runtime_error("Invalid data in input file");
This will keep reading a pass from the file until reading fails, either because it reached the end of the file, or because there was some bad data in the file. If there is bad data it throws an exception.
#include<string>
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime;
double serviceTime;
int classType;
friend std::istream & operator >>(std::istream &in, pass &p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
int main(){
string fileName;
string line;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName.c_str(), ifstream::in);
vector<pass> passes;
pass Pass;
while (fin>>Pass)
passes.push_back(Pass);
for(vector<pass>::const_iterator iter = passes.begin();
iter != passes.end();
++iter)
std::cout<<iter->arrivalTime<<" "<<iter->serviceTime<<" "
<<iter->classType<<std::endl;
}
Here is hint;
string line;
string temp;
string::size_type sz;
while (getline(cin, line))
{
istringstream ss( line );
getline( ss, temp, ',' );
double arrivalTime = stod(temp, &sz);
getline( ss, temp, ',' );
double serviceTime = stod(temp, &sz);
getline( ss, temp, ',' );
double classType = stod(temp, &sz);
cout << arrivalTime << ' '
<< serviceTime << ' '
<< classType << endl;
}

Use getline to split a string and check for int

I have a file:
name1 8
name2 27
name3 6
and I'm parsing it into vector. This is my code:
int i=0;
vector<Student> stud;
string line;
ifstream myfile1(myfile);
if (!myfile1.is_open()) {return false;}
else {
while( getline(myfile1, line) ) {
istringstream iss(line);
stud.push_back(Student());
iss >> stud[i].Name >> stud[i].Grade1;
i++;
}
myfile1.close();
}
I need to check if the stud[i].Grade1 is int. If it isn't it return false.
File can contain:
name1 haha
name2 27
name3 6
How can I do it?
EDIT:
I have tried another way (without getline) and it seems to work. I don't understand why :/
int i=0;
vector<Student> stud;
ifstream myfile1(myfile);
if (!myfile1.is_open()) {return false;}
else {
stud.push_back(Student());
while( myfile1 >> stud[i].Name ) {
if(!(myfile1 >> stud[i].Points1)) return false;
i++;
stud.push_back(Student());
}
myfile1.close();
}
If type of Grade1 in numerical such as int, Use std::istringstream::fail() :
// ...
while( getline(myfile1, line) ) {
istringstream iss(line);
stud.push_back(Student());
iss >> stud[i].Name;
iss >> stud[i].Grade1;
if (iss.fail())
return false;
i++;
}
myfile1.close();
}
// ...
It could look like this:
std::vector<Student> students;
std::ifstream myfile1(myfile);
if (!myfile1.is_open())
return false;
std::string line;
while (std::getline(myfile1, line))
{
// skip empty lines:
if (line.empty()) continue;
Student s;
std::istringstream iss(line);
if (!(iss >> s.Name))
return false;
if (!(iss >> s.Grade1))
return false;
students.push_back(s);
}
just note that iss >> s.Grade1 will succeed not only for decimal, but also for octal and hexadecimal numbers too. To make sure that only decimal value will be read, you could read it into the temporary std::string object and validate it before you use it to retrieve the number. Have a look at How to determine if a string is a number with C++?

File IO reading multiple types from a file

Lets say I have a text file like this
6 3
john
dan
lammar
I can read the numbers, and i can read the names only if they are in separate files. But here the numbers and the names are in a one file. How do i ignore the first line and start reading straight from the second one ?
int main()
{
vector<string> names;
fstream myFile;
string line;
int x,y;
myFile.open("test.txt");
//Im using this for reading the numbers
while(myFile>>x>>y){}
//Would use this for name reading if it was just names in the file
while(getline(myFile,line))
names.push_back(line);
cout<<names[0];
return 0;
}
I am not sure whether I got you right but if you always want to skip the first line - you can simply skip it?
int main()
{
vector<string> names;
fstream myFile;
string line;
int x,y;
myFile.open("test.txt");
//skip the first line
myFile>>x>>y;
//Would use this for name reading if it was just names in the file
while(getline(myFile,line))
names.push_back(line);
cout<<names[0];
return 0;
}
If you're using fstream just call the ignore() method:
istream& ignore ( streamsize n = 1, int delim = EOF );
so it became very easy:
ifstream file(filename);
file.ignore(numeric_limits<streamsize>::max(), '\n'); // ignore the first line
// read the second line
string name; getline(flie, name);
Try something like this:
int main()
{
std::vector<std::string> names;
std::fstream myFile;
myFile.open("test.txt");
if( myFile.is_open() )
{
std::string line;
if (std::getline(myFile, line))
{
std::istringstream strm(line);
int x, y;
strm >> x >> y;
while (std::getline(myFile, line))
names.push_back(line);
}
myFile.close();
if( !names.empty() )
std::cout << names[0];
}
return 0;
}