I thought I had this completely worked out, but it isn't actually writing anything to the file. It is opening the employeesOut.txt file but not writing anything. Any guesses? I'm getting the error
Here is the input file as requested.
123,John,Brown,125 Prarie Street,Staunton,IL,62088
124,Matt,Larson,126 Hudson Road,Edwardsville,IL,62025
125,Joe,Baratta,1542 Elizabeth Road,Highland,IL,62088
126,Kristin,Killebrew,123 Prewitt Drive,Alton,IL,62026
127,Tyrone,Meyer,street,999 Orchard Lane,Livingston,62088
libc++abi.dylib: terminating with uncaught exception of type std::invalid_argument: stoi: no conversion
(lldb)
I believe the error is within my main.cpp, so here that is.
#include <iostream>
#include <string>
#include <fstream>
#include "Employee.h"
using namespace std;
bool openFileForReading(ifstream& fin, const string& filename);
bool openFileForWriting(ofstream& fout, const string& filename);
int readFromFile(ifstream& in, Employee empArray[]);
void writeToFile(ofstream& out, const Employee empArray[], const int numberofEmployees);
int main(){
ifstream fin;
ofstream fout;
if(!openFileForReading(fin, "employeesIn.txt")) {
cerr << "Error opening employeesIn.txt for reading." << endl;
exit(1);
}
if(!openFileForWriting(fout, "employeesOut.txt")) {
cerr << "Error opeing employeesOut.txt for writing." << endl;
exit(1);
}
Employee employeeArray[50];
int employeeCount = readFromFile(fin, employeeArray);
fin.close();
writeToFile(fout, employeeArray, employeeCount);
fout.close();
cout << "Program successful." << endl << endl;
return 0;
}
bool openFileForReading(ifstream& fin, const string& filename) {
fin.open("employeesIn.txt");
return (fin.is_open());
}
bool openFileForWriting(ofstream& fout, const string& filename) {
fout.open("employeesOut.txt");
return (fout.is_open());
}
int readFromFile(ifstream& in, Employee empArray[]) {
int temp = 0;
string eidText;
string first;
string last;
string street;
string city;
string state;
string zipcode;
while(!in.eof()) {
getline(in, eidText, ',');
getline(in, first, ',');
getline(in, last, ',');
getline(in, street, ',');
getline(in, city, ',');
getline(in, state, ',');
getline(in, zipcode, ',');
empArray[temp].setEid(stoi(eidText));
empArray[temp].setName(first, last);
empArray[temp].setAddress(street, city, state, zipcode);
temp++;
}
return temp;
}
void writeToFile(ofstream& out, const Employee empArray[], const int numberOfEmployees) {
for (int i = 0; i < numberOfEmployees; i++){
out << "Employee Record: " << empArray[i].getEid()
<< endl
<< "Name: " << empArray[i].getName()
<< endl
<< "Home Address: " << empArray[i].getAddress()
<< endl
<< endl;
}
}
Your initial problem is solved by changing your getline(in, zipcode, ','); line to getline(in, zipcode, '\n');, since this will make getline end on the linebreak character. The error is generated when stoi is given 'Matt' as an argument.
However, your readFromFile function is receiving a copy of your array, and so the changes it makes aren't moved back into your main() function (since you're returning the count, not the array).
Since passing an array of references is messy, try instead using a std::vector (after #includeing <vector>) to group your individual employees. If Employee.H specifies a parameter-less constructor or no constructor (and you have a default constructor from your compiler), then
std::vector<Employee> employeeVector;
void readFromFile(ifstream& in, std::vector<Employee> &empVec);
void readFromFile(ifstream& in, std::vector<Employee> &empVec) {
// int temp = 0; unneeded.
string eidText;
...
string zipcode;
while(!in.eof()) {
getline(in, eidText, ',');
...
getline(in, zipcode, '\n');
Employee tempEmp();
tempEmp.setEid(stoi(eidText));
...
empVec.push_back(tempEmp);
// temp++; no longer need this
}
}
might be a better option. Even if the constructor is horribly complicated, it's still likely this would be simpler than arrays of references (you'd have to change the Employee tempEmp(); line though. Incidentally, if this complains and won't compile, I probably hit the MVP error - remove the ()s and you'll be fine.
Edit:
Since you've been instructed to use an array of Employees, you can choose to return the array, if you wish, or alternatively passing it by pointer should be legal. This would require that you iterate through by moving the pointer along the array in a similar manner to your original code. I've left the vector approach above, since I think it's cleaner, but since you can't use it, you can choose from the two alternatives (return array, pass pointer instead of object). My guess is you're supposed to opt for the pointer, since this now appears to be a homework-style problem.
In that case, you can use
int main() {
...
Employee* empPtr = employeeArray;
...
}
void readFromFile(ifstream& in, Employee* empPtr) {
string eidText;
...
string zipcode;
while(!in.eof()) {
getline(in, eidText, ',');
...
getline(in, zipcode, '\n');
(*empPtr).setEid(stoi(eidText));
...
empPtr++;
}
}
Related
I am trying to read each line of a text file in C++. I have it working for the first line in a text file, but how do I read the others? Also, I am a C++ super noob, please don't roast me.
Code:
void Grade::readInput()
{
ifstream infile("Grades.txt");
if (infile.good())
{
string sLine;
getline(infile, sLine);
cout << "This is the val " << sLine << endl;
}
infile.close();
}
This is the output I want:
This is the val 12
This is the val 17
This is the val 1
This is the val 29
I'll give you some hints - this is probably more like CodeReview.SE now...
I would recommend
separating the reading from printing
treating the grades as numbers (e.g. int or double) so you can actually work with them, and have some validation that you're not reading non-sense
Use idiomatic loops, don't just if (infile.good()) - e.g. you can't tell whether you've reached end-of-file before you try to read
Fixing up the interface, I'd suggest something like this
struct Grade {
void readInput(std::string fname);
void print(std::ostream& os = std::cout) const;
private:
static constexpr auto max_line_length = std::numeric_limits<ssize_t>::max();
std::vector<int> grades;
};
Note that readInput reads into grades without printing anything. Note also that it takes the name of the file to read as the argument, instead of hardcoding some filename.
int main() {
Grade grading;
grading.readInput("grades.txt");
grading.print();
}
This would be the main program.
The simplified/extended version of readGrades could be:
void Grade::readInput(std::string fname) {
std::ifstream infile(fname);
infile.ignore(max_line_length, '\n'); // ignore the first line
int grade = 0;
while (infile >> grade) {
grades.push_back(grade);
infile.ignore(max_line_length, '\n'); // ignore rest eating newline
}
}
Note how we ignore lines or parts thereof that we are not interested in. For extra control consider disabling white-space skipping:
infile >> std::nowskipws;
The print function could be a simple:
void Grade::print(std::ostream& os) const {
os << "Grades:";
for (int g : grades) {
os << " " << g;
}
os << std::endl;
}
Full Demo
Live On Coliru
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
struct Grade {
void readInput(std::string fname);
void print(std::ostream& os = std::cout) const;
private:
static constexpr auto max_line_length = std::numeric_limits<ssize_t>::max();
std::vector<int> grades;
};
int main() {
Grade grading;
grading.readInput("grades.txt");
grading.print();
}
void Grade::readInput(std::string fname) {
std::ifstream infile(fname);
infile.ignore(max_line_length, '\n'); // ignore the first line
int grade = 0;
while (infile >> grade) {
grades.push_back(grade);
infile.ignore(max_line_length, '\n'); // ignore rest eating newline
}
}
void Grade::print(std::ostream& os) const {
os << "Grades:";
for (int g : grades) {
os << " " << g;
}
os << std::endl;
}
Prints
Grades: 12 17 1 29
Given grades.txt:
Ignore the first line
12
17
1
29
A simple version:
std::string line1;
std::string line2;
std::string line3;
std::string line4;
std::getline(infile, line1);
std::getline(infile, line2);
std::getline(infile, line3);
std::getline(infile, line4);
With a loop:
static const unsigned int LINES_TO_READ = 3;
std::string line1_ignored;
std::getline(infile, line1_ignored);
for (unsigned int i = 0; (i < LINES_TO_READ); ++i)
{
std::string text_line;
if (std::getline(infile, text_line))
{
std::cout << text_line << std::endl;
}
else
{
break;
}
}
Both versions read the first line. Ignore the contents in the variable if you wish.
The simple method reads each text line, into separate variables.
The second reads text lines using a known-quantity loop.
I'm trying to take the profile info(username, email, etc.) from one directory and put it in another. I've been debugging the code for this program, and while there are no errors, the program won't run, saying that the program "has stopped working". I have already looked on this website and others for any possible answers, and found none.
#include <string>
#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <iomanip>
#include <filesystem>
using namespace std;
class path{
public:
string parent_directory;
string root_directory;
};
class Data{
public:
string userName;
string nickName;
string fName;
string arena_FName;
string lName;
string arena_LName;
string email;
string arenaEmail;
friend std::istream& operator>>(std::istream& input, Data& d);
};
std::istream& operator>>(std::istream& input, Data& d){
std::getline(input, d.userName);
std::getline(input, d.nickName);
//...
std::getline(input, d.arenaEmail);
return input;
}
int main(){
ifstream myfile("myfunk.txt", ios::in);
ofstream arena("arena.txt");
myfile.open("myfunk.txt", ios::in);
if(myfile){
cout << "Input file open." << endl;
}
arena.open("arena.txt", ios::out | ios::app);
if(arena){
cout << "Output file open." << endl;
}
cout << "file opening test: success" << endl;
int x = 0;
int y = 4101; //Total number of users in the directory.
int z = 0; //For inputting the required lines of info for each profile.
int profile = 0;
bool valid = false;
string role;
//string arenaRole;
bool post = false;
string line;
string p = "This PC/..."; //Path to the folder of the individual pictures.
//myVar.save("...");
string p = "...";
path path1;
path root_directory;
path parent_directory;
//bool is_directory(const std::filesystem::path& p, std::error_code& ec) noexcept; //Checks if current location is a directory.
//bool postPic;
const unsigned int MAXIMUM_DATA = 4100u;
Data database[MAXIMUM_DATA];
cout << "All variables but the filesystem have been accepted! Please install this program on the network." << endl;
while(x < y){
cout << "Primary loop functioning" << endl;
if(post = true){
getline(myfile, line); //Grab and read next line.
myfile >> line;
line = userName[x];
arena << "Username: " << userName[x] << "\n";
z++;
getline(myfile, line);
myfile >> line;
line = role[x];
arena << "Role: " << role[x] << "\n";
z++;
getline(myfile, line);
line = nickName[x];
myfile >> nickName[x];
arena << "nickname: " << nickName[x] << "\n";
z++;
getline(myfile, line);
line = fName[x];
myfile >> fName;
arena << "First Name: " << fName[x] << "\n";
z++;
getline(myfile, line);
line = lName[x];
myfile >> lName;
arena << "Last Name: " << lName[x] << "\n";
z++;
getline(myfile, line);
myfile >> line;
line = email[x];
arena << "Email: " << email[x] << "\n";
getline(myfile, line);
z = 0; //Next profile...
}
int data;
while(myfile >> data){
if(nickName[x] = NULL){
myfile >> "<Error> Some required information is missing! Contact user! </Error> /n";
valid = false;
post = false;
x++;
}
if(email[x] != NULL){
std::string str("#");
std::string str2(".com");
std::string str3(".net");
std::string str4(".edu");
if(std::size_t found = email[x].find(str) & (std::size_t found = email[x].find(str2) || std::size_t found = email[x].find(str3) || std::size_t found = email[x].find(str4)){
valid = true;
if(valid = true){
post = true;
}
}
else{
valid = false;
post = false;
x++;
}
}
}
}
}
}
x++;
}
//x++;
myfile.close(); //Closes the file in the directory.
arena.close(); //Closes the file in Arena.
return 0;
}
Let's rework your code.
First, let's create a data structure for the data:
class Data
{
public:
string userName;
string nickName;
string fName;
string arena_FName;
string lName;
string arena_LName;
string email;
string arenaEmail;
};
If you need an array for the data, it would be declared as:
const unsigned int MAXIMUM_DATA = 4100u;
Data database[MAXIMUM_DATA];
Next, let's overload the extraction operator>> to make reading easier:
class Data
{
public:
//...
friend std::istream& operator>>(std::istream& input, Data& d);
};
std::istream& operator>>(std::istream& input, Data& d)
{
std::getline(input, d.userName);
std::getline(input, d.nickName);
//...
std::getline(input, d.arenaEmail);
return input;
}
This simplifies your input loop to:
std::vector<Data> database;
Data d;
while (my_file >> d)
{
database.push_back(d);
}
You can query the amount of data read in by using the std::vector::size() method, i.e. database.size().
Also, you don't need a separate structure for a file path. A simple std::string will suffice. I recommend using forward slash, '/', because it is recognized by both Windows and *nix operating systems and won't be interpreted as an escape character.
I am getting an error while trying to parse a stringstream when reading from a file, I cannot seem to figure what the issue is.
the error I am receiving is:
no matching function for call to 'getline'
std::getline(parse_input, intensity, ',');
this repeats for each variable I am trying to parse.
the following is my code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
int main(int argc, char const *argv[])
{
std::fstream fs;
fs.open("test.dat");
std::string* configuration_record;
std::string temp_count;
size_t no_of_records;
if(!fs.is_open())
{
std::cerr << "File failed to open" << std::endl;
}
else
{
while(!fs.eof())
{
std::getline(fs, temp_count);
no_of_records++;
}
fs.clear();
fs.seekp(0);
}
std::string stimulation_type;
std::string stimulation_name;
double intensity;
double frequency;
double duration;
std::string location;
configuration_record = new std::string[no_of_records];
for(size_t i=0; i<no_of_records; i++)
{
std::getline(fs, configuration_record[i]);
}
for(size_t i=0; i<no_of_records; i++)
{
size_t found;
found = configuration_record[i].find("stim");
if(found != std::string::npos)
{
std::stringstream parse_input(configuration_record[i]);
std::getline(parse_input, stimulation_type, ',');
std::getline(parse_input, stimulation_name, ',');
std::getline(parse_input, location, ',');
std::getline(parse_input, intensity, ',');
std::getline(parse_input, frequency, ',');
std::getline(parse_input, duration, ',');
std::cout << stimulation_name << "," <<location << intensity << "," << frequency << "," << duration
<< std::endl;
}
else
{
std::stringstream parse_input(configuration_record[i]);
std::getline(parse_input, stimulation_type, ',');
std::getline(parse_input, stimulation_name, ',');
std::getline(parse_input, intensity, ',');
std::getline(parse_input, duration, ',');
std::cout << stimulation_name << "," << intensity << "," << duration
<< std::endl;
}
}
return 0;
}
The declarations for getline from the manual are:
istream& getline (istream& is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);
istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);
As you see, they accept an istream (stringstream is valid), however the second argument has to be a string, so when you pass double (intensity, frequency and duration), you are calling an inexistent function.
you can use a temporary string and then save it to a double, using stod:
std::string temp;
double intensity;
std::getline(parse_input, temp, ',');
intensity= std::stod (temp);
Here is my code for an assignment I have. Whenever I try and compile I get an error for my read function due to something in "ios_base.h" I am not sure what to do and/or if my code does the intended function of taking a file and moving it's elements into a separate file that has the name and average next to each other.
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
using namespace std;
struct Student
{
string fname;
string lname;
double average;
};
int read(ifstream, Student s[]);
void print(ofstream fout, Student s[], int amount);
int main()
{
const int size = 10;
ifstream fin;
ofstream fout;
string inputFile;
string outputFile;
Student s[size];
cout << "Enter input filename: ";
cin >> inputFile;
cout << "Enter output filename: ";
cin >> outputFile;
cout << endl;
fin.open(inputFile.c_str());
fout.open(outputFile.c_str());
read(fin , s);
print(fout, s, read(fin, s));
}
int read(ifstream fin, Student s[])
{
string line;
string firstName;
string lastName;
double score;
double total;
int i=0;
int totalStudents=0;
Student stu;
while(getline(fin, line)){
istringstream sin;
sin.str(line);
while(sin >> firstName >> lastName){
stu.fname = firstName;
stu.lname = lastName;
while(sin >> score){
total *= score;
i++;
}
stu.average = (total/i);
}
s[totalStudents]=stu;
totalStudents++;
}
return totalStudents;
}
void print(ofstream fout, Student s[], int amount)
{
ostringstream sout;
for(int i = 0; i<amount; i++)
{
sout << left << setw(20) << s[i].lname << ", " << s[i].fname;
fout << sout << setprecision(2) << fixed << "= " << s[i].average;
}
}
Stream objects are not copyable. Their copy constructor is deleted. They must be passed by reference, not by value:
int read(ifstream &, Student s[]);
void print(ofstream &fout, Student s[], int amount);
etc...
Sam Varshavchik's answer is correct, but he didn't mention why stream objects don't allow you to copy them.
The issue here is that a stream object owns a buffer, and buffers can't be copied safely.
To take an example, suppose you have data coming in over a network socket and a buffer sitting in front of it, and you copy this buffered reader. If you read from the copy, it will read some indeterminate amount of data and put it into the buffer. This data is now gone from the network socket and only exists in the buffer. Now suppose you read from the copy. Then you'll get some indeterminate amount of data that came after the data you read in the original. Going back and forth in this way, you'd get two "streams" with gaps in them where the other reader was reading the data.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
my build is totally successful here, yet not outputting to my text file, I know I asked a question a few days ago about this program, and I've since changed it. What am I doing wrong now?
Thanks in advance guys.
I'm trying to input from an employeesIn.txt file and create an employeesOut.txt file made of employee structures.
Here's my text file.
123,John,Brown,125 Prarie Street,Staunton,IL,62088
124,Matt,Larson,126 Hudson Road,Edwardsville,IL,62025
125,Joe,Baratta,1542 Elizabeth Road,Highland,IL,62088
126,Kristin,Killebrew,123 Prewitt Drive,Alton,IL,62026
127,Tyrone,Meyer,street,999 Orchard Lane,Livingston,62088
The output should look like
Employee Record: 123
Name: John Brown
Home Address: 125 Prarie Street
Staunton, IL 62088
Employee Record: 124
Name Matt Larson
Home Address:.... and so on
Here's my code.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
struct Person {
string first;
string last;
};
struct Address {
string street;
string city;
string state;
string zipcode;
};
struct Employee {
Person name;
Address homeAddress;
int eid;
};
int readEmployee(istream& in, Employee eArray[]);
void displayEmployee(ostream& out,Employee eArray[], int EmployeePopulation);
const int arr=50;
Employee eArray[arr];
ifstream fin;
ofstream fout;
int main(int argc, const char * argv[])
{
fin.open("employeesIn.txt");
if (!fin.is_open()) {
cerr << "Error opening employeesIn.txt for reading." << endl;
exit(1);
}
fout.open("employeesOut.txt");
if (!fout.is_open()) {
cerr << "Error opening employeesOut.txt for writing." << endl;
exit(1);
}
int tingle = readEmployee(fin, eArray);
fin.close();
displayEmployee(fout, eArray, tingle);
fout.close();
exit(0);
}
int readEmployee(istream& in, Employee eArray[])
{
string eidText;
string line;
getline(in, line);
int EmployeePopulation = 0;
while (!in.eof()) {
getline(in, eidText, ',');
eArray[EmployeePopulation].eid = stoi(eidText);
getline(in, eArray[EmployeePopulation].name.first, ',');
getline(in, eArray[EmployeePopulation].name.last, ',');
getline(in, eArray[EmployeePopulation].homeAddress.street, ',');
getline(in, eArray[EmployeePopulation].homeAddress.city, ',');
getline(in, eArray[EmployeePopulation].homeAddress.state, ',');
getline(in, eArray[EmployeePopulation].homeAddress.zipcode);
EmployeePopulation++;
}
return EmployeePopulation;
}
void displayEmployee(ostream& out, Employee eArray[], int EmployeePopulation)
{
for (int i = 0; i <= EmployeePopulation - 1; i++) {
out << "Employee Record: " << eArray[i].eid
<< endl
<< "Name: " << eArray[i].name.first << " " << eArray[i].name.last
<< endl
<< "Home address: " << eArray[i].homeAddress.street
<< endl
<< eArray[i].homeAddress.city << ", " << eArray[i].homeAddress.state << " " << eArray[i].homeAddress.zipcode
<< endl
<< endl;
}
}
Two things:
You should use return 0 rather than exit(0) at the end of main.
Checking for eof after you have performed several reads and tried to convert the data is wrong. You need to check for failure of the reads themselves.
This corrects the eof issue. The program was crashing for me because stoi threw an exception when the read failed.
int readEmployee(istream& in, Employee eArray[])
{
string eidText;
string line;
//This discards the first line. Incorrect for the test data you supplied.
getline(in, line);
int EmployeePopulation = 0;
//Check for errors while reading, not eof after the fact.
//This was crashing because stoi failed when no data was
//read due to eof being true after the loop check.
while( getline(in, eidText, ',') &&
getline(in, eArray[EmployeePopulation].name.first, ',') &&
getline(in, eArray[EmployeePopulation].name.last, ',') &&
getline(in, eArray[EmployeePopulation].homeAddress.street, ',') &&
getline(in, eArray[EmployeePopulation].homeAddress.city, ',') &&
getline(in, eArray[EmployeePopulation].homeAddress.state, ',') &&
getline(in, eArray[EmployeePopulation].homeAddress.zipcode))
{
eArray[EmployeePopulation].eid = stoi(eidText);
EmployeePopulation++;
}
return EmployeePopulation;
}
You would have fewer problems if you used a vector of Employee.
You could pass it by reference to the functions.
The functions could get the number of employees by using std::vector::size().
The std::vector automatically expands when using the push_back method.
If you created input and output methods for your classes, you wouldn't have to violate encapsulation:
class Person // using class to support privacy and encapsulation
{
std::string first_name;
std::string last_name;
public:
friend std::istream& operator>>(std::istream& inp, Person& p);
friend std::ostream& operator<<(std::ostream& out, const Person& p);
};
std::istream& operator>>(std::istream& inp, Person& p)
{
std::getline(inp, p.first_name, ',');
std::getline(inp, p.last_name, ',');
}
std:ostream& operator<<(std::ostream& out, const Peron& p)
{
out << "Name: ";
out << p.first_name;
out << " ";
out << p.last_name;
}
class Employee
{
Person name;
Address addr;
public:
friend std::istream& operator>>(std::istream& inp, Employee& e);
};
std::istream& operator>>(std::istream& inp, Employee& e)
{
inp >> name;
inp >> addr;
};
The missing formatted input and output are left as an exercise for the reader.
In your readEmployee function, you passed isstream& in.I suppose you should be checking for while (!in.eof()) ans not while (!fin.eof()).
And your getline(fin, line); should be getline(in, line); too.