ifstream still reading after eof - c++

I am writing a program that is going to have to import data from a file into various containers. I have it importing everything properly, but it is continuing to read after what is supposed to be the eof. I have a feeling I am not properly telling the loop when to end, but the code is below for everyone to look at.
bool InitLoad(vector<string>&num, vector<string>&name, vector<double>&price, vector<char>&tax)
{
ifstream invFile;
int intTemp;
string strTemp;
double dubTemp;
char chTemp;
string fileLoc = "C:/Users/owner/Documents/Visual Studio 2010/Projects/CISS 350/Week 1 Grocery Register/Week 1 Grocery Register/Invent.dat";
//Open Invent.dat file. Location below is the location used on creators computer. Other may need to modify file location
invFile.open(fileLoc.c_str(), ios::in);
//If Invent.dat file fails to open display error message and return false
if(invFile.fail())
{
cout << "Could not open inventory file" << endl;
return false;
}
if(invFile)
{
//Read first line of the file
getline(invFile, strTemp, ' ');
while(invFile) //while invFile contains data display import the list
{
cout << strTemp << " ";
num.push_back(strTemp);
getline(invFile, strTemp, ' ');
cout << strTemp << " ";
name.push_back(strTemp);
getline(invFile, strTemp, ' ');
dubTemp = atof(strTemp.c_str());
cout << dubTemp << " ";
price.push_back(dubTemp);
invFile.get(chTemp);
cout << chTemp;
tax.push_back(chTemp);
getline(invFile, strTemp, ' ');
}
}
invFile.close();
cout << endl;
//Verify Proper input...REMOVE WHEN COMPLETE
cout << "Verifying input data correct..." << endl;
int vecSize = num.size();
cout << vecSize << endl;
for(int i = 0; i < vecSize; i++)
{
cout << num[i] << " " << name[i] << " " << price[i] << " " << tax[i] << endl;
}
}

Your check does not check eof flag
http://www.cplusplus.com/reference/ios/ios/operator_bool/
use invFile.eof() instead
And also eof flag would be setted after reading past EOF
PS: OMG!! do not use atof, just do invFile << dubTemp

Since your data is space separated you can uses formatted input instead of getline() on every string.
Something along the lines of this.
string lineTemp;
while(getline(invFile, lineTemp)) //while invFile contains data display import the list
{
string strTemp1, strTemp1, dubTemp, chTemp;
istringstream lstr(lineTemp);
if(lstr >> strTemp1 >> strTemp2 >> dubTemp >> chTemp) {
num.push_back(strTemp1);
name.push_back(strTemp2);
price.push_back(dubTemp);
tax.push_back(chTemp);
cout << strTemp1 << " "
<< strTemp2 << " "
<< dubTemp << " "
<< chTemp << endl;
}
else {
// Something is wrong with the line format.
}
}
This will read the data in a formated way and to the proper type. Plus you don't have to worry about empty lines or extra characters in lines.

Related

Changing the first line of file and going to the last without losing data c++

I'm working on a school project for C++ with involves on working with polimorfism, dynamic objects, heritage. My project consists on an main.cpp with will run a loop with options for the user to select. I will be storing some data in a file, the data will be appended so I don't loose the data that was already in the file, but my file works like the following example:
2
Data;12;21;0
Data2s;22;61;7
This is what I want to happen:
3
Data;12;21;0
Data2s;22;61;7
Data3s;8;2;9
What I'm attempting to do is, I have to change the first line to the next variable number, and I can't loose the data under it.
After I change the line, I will go to the end of the file (after numb 7) so I can add another line of data.
attempt 1:
void Empresa::save_data(){
ofstream arquivoE;
arquivoE.open("data.txt", ios::app);
if (temp > 0){
arquivoE.seekp(0) << num_func;
arquivoE.seekp(0, ios_base::end);
arquivoE << endl;
}else{
arquivoE << num_func << endl;
}
for (int i = temp; i < num_func; i++)
{
arquivoE << func[i]->getNome() << ";" << func[i]->getEmissao() << ";" << func[i]->getSalario() << ";" << func[i]->last_field() << endl;
}
cout << " Sucesso ao gravar arquivo!" << endl;
arquivoE.close();
}
attempt 2:
void Empresa::save_data(){
ofstream arquivoE;
arquivoE.open("data.txt", ios::out);
arquivoE.seekp(0, ios::beg) << num_func;
arquivoE.seekp(0, ios::end);
arquivoE << endl;
for (int i = temp; i < num_func; i++)
{
arquivoE << func[i]->getNome() << ";" << func[i]->getEmissao() << ";" << func[i]->getSalario() << ";" << func[i]->last_field() << endl;
}
cout << " Sucesso ao gravar arquivo!" << endl;
arquivoE.close();
}
num_func is the the variable from the first line, which I need to change
Temp variable is just to keep track of where to add the line
This is the data line:
arquivoE << func[i]->getNome() << ";" << func[i]->getEmissao() << ";" << func[i]->getSalario() << ";" << func[i]->last_field() << endl;

How to edit the specific line in a text file?

How should I edit the specific line in a text file? And how should I avoid overwriting issue. ( How do I keep the record I had added before instead of replacing them by the new records?)
I tried to use line.replace but it says " No matching member function for call to replace ".
else if(choice == 4){
// count++;
string edit;
string newdate;
double newincome;
double newoutcome;
double EditTodayBalance = 0;
string emptySpace = " ";
// string sentence;
cout << " There are " << count << " record(s) in the file " << endl;
cout << " Please enter the date to edit " << endl;
cin >> edit;
size_t pos;
ifstream Record("BankRecord.txt");
if (Record.is_open()) {
while (getline(Record, line)) {
pos = line.find(edit);
if (pos != string::npos) // string::npos is returned if
string is not found
{
cout << line << endl;
cout << " Enter what you want to replace " << endl;
cout << " Please enter the new date you want to put " << endl;
cin >> newdate;
cout << " Please enter the new income you want to put " << endl;
cin >> newincome;
cout << " Please enter the new outcome you want to put " << endl;
cin >> newoutcome;
cout << " Your new Record is " << endl;
cout << count << emptySpace << newdate << emptySpace << newincome << emptySpace << newoutcome << endl;
//line.replace()
}
}
}
EditTodayBalance = newincome - newoutcome;
cout << " Today's balance is " << EditTodayBalance << endl;
cout << " Please reenter your choice " << endl;
cin >> choice;
}
I expect if the old line is " 1 2/2/2019 32 21 " and I input the new line to be " 1 2/3/2019 22 11 ". Then when I open the file the record will be the new one.
I'm afraid you will have to re-write the entire file. Read the content of the file line by line, store it in memory (maybe a vector of strings). Now do your editing on the specified line in that vector. When the operation is complete, dump the entire content of vector in another file. You may later replace the original file.

Reading from CSV file with C++

I was trying to read the records from a CSV file. This code is froom GeeksforGeeks. I tried compiling it. Then when I try to run it terminates with an exception
void read_record()
{
// File pointer
fstream fin;
// Open an existing file
fin.open("reportcard.csv", ios::in);
// Get the roll number
// of which the data is required
int rollnum, roll2, count = 0;
cout << "Enter the roll number "
<< "of the student to display details: ";
cin >> rollnum;
// Read the Data from the file
// as String Vector
vector<string> row;
string line, word, temp;
while (fin >> temp) {
row.clear();
// read an entire row and
// store it in a string variable 'line'
getline(fin, line);
// used for breaking words
stringstream s(line);
// read every column data of a row and
// store it in a string variable, 'word'
while (getline(s, word, ',')) {
// add all the column data
// of a row to a vector
row.push_back(word);
}
// convert string to integer for comparision
roll2 = stoi(row[0]);
// Compare the roll number
if (roll2 == rollnum) {
// Print the found data
count = 1;
cout << "Details of Roll " << row[0] << " : \n";
cout << "Name: " << row[1] << "\n";
cout << "Maths: " << row[2] << "\n";
cout << "Physics: " << row[3] << "\n";
cout << "Chemistry: " << row[4] << "\n";
cout << "Biology: " << row[5] << "\n";
break;
}
}
if (count == 0)
cout << "Record not found\n";
}
Program terminates with a message
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi"
your current error is that row[0] is not a string. stoi expects a string. invalid argument means the value you gave a function inside the parenthesis is not the value that the computer expects. i might try printing the variable of one line and checking the type with
cout << typeid(row[0]).name.()
It also is best practice to do things incrementally when coding (walk before you can run.) try reading in one cell and closing if youre struggling with that. then one line. then multiple lines. its easy enough to comment out lines of code. many text editor even have a hotkey to comment out lines of code. try googling
block comment hotkey [your text editor name]
void read_record()
{
// File pointer
fstream fin;
// Open an existing file
fin.open("reportcard.csv", ios::in);
// Get the roll number
// of which the data is required
int rollnum, count = 0;
cout << "Enter the roll number "
<< "of the student to display details: ";
cin >> rollnum;
ostringstream roll2;
roll2 << rollnum;
string roll = roll2.str();
//cout << roll;
// Read the Data from the file
// as String Vector
vector<string> row;
string line, word, temp;
while (!fin.eof()) {
row.clear();
// read an entire row and
// store it in a string variable 'line'
getline(fin, line);
// used for breaking words
stringstream s(line);
// read every column data of a row and
// store it in a string variable, 'word'
while (getline(s, word, ',')) {
// add all the column data
// of a row to a vector
row.push_back(word);
}
// convert string to integer for comparision
//roll2 = stoi(row[0]);
//cout << row[0] << row[1] << row[2] << row[3] << row[4] << row[5] << "\n";
// Compare the roll number
if (roll == row[0]) {
// Print the found data
count = 1;
cout << "Details of Roll " << row[0] << " : \n";
cout << "Name: " << row[1] << "\n";
cout << "Maths: " << row[2] << "\n";
cout << "Physics: " << row[3] << "\n";
cout << "Chemistry: " << row[4] << "\n";
cout << "Biology: " << row[5] << "\n";
break;
}
}
if (count == 0)
cout << "Record not found\n";
}

Issue with saving an edited infile to an outfile in c++

Im having an issue with the last section of coding on here. The // Copy files from infile to outfile. The program transfers my infile which is simply a an 8 digit number , 20392207,splits it up into individual digits using the .at method; and is supposed to save that output to an outfile. I cant figure out how to save the output to the outfile. Any advice?
infile looks as follows
20392207
program output looks like this
The input number :20392207
The number 1:2
The number 2:0
The number 3:3
The number 4:9
The number 5:2
The number 6:2
The number 7:0
The number 8:7
outfile is supposed to look like the program out put, but instead just looks like an exact copy of the infile.
#include<iostream>
#include<fstream>
#include<cstdlib>
#include<string>
#include<cmath>
using namespace std;
int main()
{
string ifilename, ofilename, line, line2;
ifstream inFile, checkOutFile;
ofstream outFile;
char response;
int i;
// Input file
cout << "Please enter the name of the file you wish to open : ";
cin >> ifilename;
inFile.open(ifilename.c_str());
if (inFile.fail())
{
cout << "The file " << ifilename << " was not successfully opened." << endl;
cout << "Please check the path and name of the file. " << endl;
exit(1);
}
else
{
cout << "The file is successfully opened." << endl;
}
// Output file
cout << "Please enter the name of the file you wish to write : ";
cin >> ofilename;
checkOutFile.open(ofilename.c_str());
if (!checkOutFile.fail())
{
cout << "A file " << ofilename << " exists.\nDo you want to continue and overwrite it? (y/n) : ";
cin >> response;
if (tolower(response) == 'n')
{
cout << "The existing file will not be overwritten. " << endl;
exit(1);
}
}
outFile.open(ofilename.c_str());
if (outFile.fail())
{
cout << "The file " << ofilename << " was not successfully opened." << endl;
cout << "Please check the path and name of the file. " << endl;
exit(1);
}
else
{
cout << "The file is successfully opened." << endl;
}
// Copy file contents from inFile to outFile
while (getline(inFile, line))
{
cout << "The input number :" << line << endl;
for (i = 0; i < 8; i++)
{
cout << "The number " << i + 1 << ":";
cout << line.at(i);
cout << endl;
}
outFile << line << endl;
}
// Close files
inFile.close();
outFile.close();
} // main
Here we can see that outFile is only written to outside of the while loop:
while (getline(inFile, line))
{
cout << "The input number :" << line << endl;
for (i = 0; i < 8; i++)
{
cout << "The number " << i + 1 << ":";
cout << line.at(i);
cout << endl;
}
}
outFile << line << endl;
It has no chance of containing the same output as the console
Solution: Write inside the loop the same stuff that was written to the console:
while (getline(inFile, line))
{
cout << "The input number :" << line << endl;
outFile << "The input number :" << line << endl;
blah blah blah
}
But this looks like crap and a function makes like a better solution by eliminating duplication and upping re-usability.
void output(std::ostream & out,
const std::string & line)
{
out << "The input number :" << line << endl;
for (int i = 0; i < 8; i++)
{
out << "The number " << i + 1 << ":";
out << line.at(i);
out << endl;
}
}
and called:
while (getline(inFile, line))
{
output(cout, line);
output(outFile, line);
}
You need to write to outFile inside the while(getline(inFile, line)) loop.
[edit] see user4581301's answer for a more thorough treatment.

Ofstream not creating a new file correctly

I've tried to write a simple database program. The problem is that ofstream does NOT want to make a new file.
Here's an extract from the offending code.
void newd()
{
string name, extension, location, fname;
cout << "Input the filename for the new database (no extension, and no backslashes)." << endl << "> ";
getline(cin, name);
cout << endl << "The extension (no dot). If no extension is added, the default is .cla ." << endl << "> ";
getline(cin, extension);
cout << endl << "The full directory (double backslashes). Enter q to quit." << endl << "Also, just fyi, this will overwrite any files that are already there." << endl << "> ";
getline(cin, location);
cout << endl;
if (extension == "")
{
extension = "cla";
}
if (location == "q")
{
}
else
{
fname = location + name + "." + extension;
cout << fname << endl;
ofstream writeDB(fname);
int n = 1; //setting a throwaway inteher
string tmpField, tmpEntry; //temp variable for newest field, entry
for(;;)
{
cout << "Input the name of the " << n << "th field. If you don't want any more, press enter." << endl;
getline(cin, tmpField);
if (tmpField == "")
{
break;
}
n++;
writeDB << tmpField << ": |";
int j = 1; //another one
for (;;)
{
cout << "Enter the name of the " << j++ << "th entry for " << tmpField << "." << endl << "If you don't want any more, press enter." << endl;
getline(cin, tmpEntry);
if (tmpEntry == "")
{
break;
}
writeDB << " " << tmpEntry << " |";
}
writeDB << "¬";
}
cout << "Finished writing database. If you want to edit it, open it." << endl;
}
}
EDIT: OK, just tried
#include <fstream>
using namespace std;
int main()
{
ofstream writeDB ("C:\\test.cla");
writeDB << "test";
writeDB.close();
return 0;
}
and that didn't work, so it is access permission problems.
ofstream writeDB(fname); //-> replace fname with fname.c_str()
If you lookup the documentation of the ofstream constructor, you will see something like:
explicit ofstream ( const char * filename, ios_base::openmode mode = ios_base::out );
The second argument is optional, but the first one is a const char*, and not a string. To solve this problem the most simple way is to convert your string to something called a C-string (char*, which is basically an array of chars); to do that just use c_str() (it«s part of the library).
Other than that, you could just place the information directly on a C-str, and then pass it normally to the ofstream constructor.