This question already has answers here:
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 1 year ago.
I have started working on a program, and for the life of me I can't find a bug which continuously searches the eof and doesn't end. It is most certainly a problem with my fileread and widthcount functions, and they most likely have the same error. Is there a fix for this? It just keeps looping.
The file input is this
12.43 62.38 Los Angeles
21 59 Columbus, Ohio
0 15.58 Green Bay, Wisconsin
This continues for another 10 lines.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
using namespace std;
void fileread(ifstream &ifFile, char newline, double &temp1, double &temp2, string city);
void widthcount(ifstream &ifFile, int &maxCount, char newline);
int main() {
double temp1, temp2, minTemp, maxTemp;
int maxCount;
char newline=' ';
string FileRead, city;
ifstream ifFile;
cout << "Please enter the location of the input file: ";
cin >> FileRead;
ifFile.open(FileRead.c_str());
if (ifFile.fail()) {
perror(FileRead.c_str());
exit(1);
}
widthcount(ifFile, maxCount, newline);
while(!ifFile.eof()) {
widthcount(ifFile, maxCount, newline);
}
ifFile.close();
ifFile.open(FileRead.c_str());
fileread(ifFile, newline, temp1, temp2, city);
while(!ifFile.eof()) {
fileread(ifFile, newline, temp1, temp2, city);
cout << left << setw(20) << city
<< right << setw(6) << fixed << setprecision(2) << temp1
<< right << setw(6) << fixed << setprecision(2) << temp2 << endl;
}
}
void fileread(ifstream &ifFile, char newline, double &temp1, double &temp2, string city) {
int charcount = 0;
ifFile >> temp1 >> temp2;
while (newline == ' ')
ifFile.get(newline);
while (newline != '\n' || !ifFile.eof()) {
charcount++;
ifFile.get(newline);
}
}
void widthcount (ifstream &ifFile, int &maxCount, char newline) {
double temp1, temp2;
int charcount = 0;
ifFile >> temp1 >> temp2;
ifFile.get(newline);
while (newline != '\n' && !ifFile.eof()) {
charcount++;
ifFile.get(newline);
cout << newline;
}
if (charcount > maxCount)
maxCount = charcount;
}
You need to check for failure (test .fail()), not end of file.
Typically .fail() is checked by using the stream object directly as condition.
E.g. while( f ) is equivalent to while( !f.fail() ).
Checking specifically for eof() is almost always a mistake -- for example, if you encounter some other error before reaching the end of the file, eof will remain false, but reading will fail (but since eof remains false, your loop will continue attempting to read and failing, forever).
Personally, I'd structure the code quite a bit differently. I'd start by creating a struct to represent one "record" from the file:
struct city {
double temp1, temp2; // horrible names, but I don't know what they represent.
std::string name;
};
Then I'd overload operator>> to extract the data for one city from a stream, and operator<< to display the data for a city:
std::istream &operator>>(std::istream &is, city &c) {
is >> c.temp1 >> c.temp2;
std::getline(is, c.name);
return is;
}
std::ostream &operator<<(std::ostream &os, city const &c) {
return cout << left << setw(20) << c.name
<< right << setw(6) << fixed << setprecision(2) << c.temp1
<< right << setw(6) << fixed << setprecision(2) << c.temp2;
}
Then I'd use those to read and write the data as god intended (with an algorithm):
int main(int argc, char **argv) {
// For the moment, cheat and take the file name from the command line
std::ifstream ifFile(argv[1]);
std::copy(std::istream_iterator<city>(ifFile),
std::istream_iterator<city>(),
std::ostream_iterator<city>(std::cout, "\n"));
return 0;
}
As an aside, I'd note that although you call widthcount, you never seem to use any result from it. At least for now, I've skipped over doing anything similar, since it didn't affect the result.
You should try something like
double t1,t2;
string s1,s2;
ifstream ifs;
ifs.open(Filename.c_str());
ifs>>t1>>t2>>s1>>s2;
while(ifs)
{
.......
do the calculations
.......
ifs>>t1>>t2>>s1>>s2;
}
This works for all the cases.If ,after the first read,ifs enters the fail state then rest of the file reading will be skipped and lines after the while loop will be executed.Other wise, rest of the lines will be read in the loop.You can change your requirement based on whether you want to read a string,a character or a line.
Related
I'm quite new to C++. I'm writing a program that reads data from a file and puts it into an object. I'm doing this to try to understand the iostream better.
Each line of the file contains an alphanumeric ID in the format nxn, where n is some integer, and x is some character, a color, another integer, and then a location. Here's an example
17832X9647 "blue" 100 "New York City"
Unfortunately, the code that I've written doesn't get all of the final string, since it has spaces in it. So, I tried writing a function that would return all of the text inside double quotes (not including the double quotes) and putting it inside the input stream, while still preserving the whitespaces and discarding the double quotes. Unfortunately, that also did not work, so now I'm sitting here scratching my head, not knowing what to do, and it's because I fundamentally don't understand how the iostream works. What can I do to fix this? I would really, really not like to restructure my whole program, because I want to learn exactly how I'm treating the iostream wrong. Getting this thing to work in the most efficient way isn't important to me, just that I understand more about the iostream.
Here is my code.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
string ReadDoubleString(istream & in)
{
string output_string;
char ch = '\"';
in.ignore(ch); //i'm thinking that this will make the extraction skip any double quotes
in >> output_string;
return output_string;
}
//begin class declaration and implementation for Widget
class Widget
{
friend istream& operator>>(istream&, Widget&);
public:
string ID;
string color;
int Age;
string location;
void setAttributes(string, string, int, string);
};
void Widget::setAttributes(string ID, string color, int Age, string location)
{
this->ID = ID;
this->color = color;
this->Age = Age;
this->location = location;
}
istream& operator>>(istream& in, Widget& output_widget)
{
int input_ID_pt1;
char input_ID_pt2;
int input_ID_pt3;
string full_ID;
string input_color;
int input_Age;
string input_location;
in >> input_ID_pt1 >> input_ID_pt2 >> input_ID_pt3;
input_color = ReadDoubleString(in);
in >> input_Age;
input_location = ReadDoubleString(in);
full_ID = to_string(input_ID_pt1) + input_ID_pt2 + to_string(input_ID_pt3);
output_widget.setAttributes(full_ID, input_color, input_Age, input_location);
return in;
}
//end class declaration and implementation for Widget
int main()
{
string file_name;
cout << "enter file name" << endl;
cin >> file_name;
ifstream newFile(file_name);
if (newFile.is_open())
{
Widget w1;
newFile >> w1;
cout << w1.ID << endl;
cout << w1.color << endl;
cout << w1.Age << endl;
cout << w1.location << endl;
}
else
{
cout << "could not open file!" << endl;
}
return 0;
}
My program ends up returning this when I use the data from earlier.
17832X9647
//empty line
0
//empty line
//empty line
Thank you so much for reading.
I am trying to figure out how to extract values from a text file as a type double so that they can be used in calculations.
I have a text file that is formatted like:
parameter1 parameter2 parameter3
50 0 0.1
And I want to extract only the numbers.
This is one of my attempts (I have been working for hours trying to figure out how to do this).
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
const int MAX = 80;
char buffer[MAX];
string input;
double j;
ifstream param0;
param0.open("param0.txt");
char ch;
while (param0)
{
param0.get(ch);
cout << ch;
}
getline(param0, input);
param0 >> j;
while (param0)
{
cout << j << endl;
getline(param0, input);
param0 >> j;
}
return 0;
}
this code
char ch;
while (param0)
{
param0.get(ch);
cout << ch;
}
runs to the end of the file. All reads after it will return nothing. Either take this loop out or rewind the file param0.rewind()
The basic idea of the code should be:
std::string line;
double p1, p2, p3;
std::string more;
while (std::getline(in, line)) {
std::istringstream iss{line};
if (iss >> p1 >> p2 >> p3 && !(iss >> more)) {
std::printf("p1=%f, p2=%f, p3=%f\n", p1, p2, p3);
} else {
std::printf("invalid line: %s\n", line.c_str());
}
}
In plain words, the code says: a line is valid if it contains three numbers and nothing more.
C++ purists will say that I shouldn't use printf in C++, but I like the separation between formatting and the actual data.
suggestion :
best to check if you open the file correctly.
close the file once you finish with it.
you can just use the >> operator if you reading everything in one line. It doesn't matter if its string or double as long as if you pass the correct storage variable;
string param1;
string param2;
string param3;
double j,k,l;
ifstream file("test.txt",std::ios::in);
if (!file.is_open())
std::cout << "failed to open " << endl;
while (file)
{
file >> param1 >> param2 >>param3; // getting as strings
cout << param1 <<", "<< param2<<", "<< param3<<endl;
file >> j >> k >> l; //getting as doubles
cout << j <<", " << k <<", " << l<<endl;
}
file.close();
return 0;
output
parameter1, parameter2, parameter3
50, 0, 0.1
The purpose of the program is to read a phrase from a file into a vector and convert the phrase into Pig Latin. When the translated phrase is outputted in Pig Latin, an additional "ay" is added after the phrase (which is not supposed to happen). Can anyone spot why this is happening? It is important that I fix this because it affects the total letters and total characters of the Pig Latin phrase that I need to output. Also, I'm not asking anyone to write any code for me, but any tips on how to make my code less redundant. A portion of my grade for programs is efficiency, which I usually lose points on.
Here's the code:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <stdio.h>
#include <ctype.h>
using namespace std;
int main()
{
ifstream in;
string word, fileName;
vector <string> phrase;
int length = 0, index = 0;
int totalWords = -1, totalLetters = -3, totalChars;
cout << "PIG LATIN PROGRAM" << endl;
cout << "Which file are you accessing? : ";
cin >> fileName;
fileName += ".txt";
in.open(fileName);
if (in.fail()) cout << "\nFile not found!" << endl;
while(getline(in, word)) phrase.push_back(word);
cout << "Original Phrase: " << phrase[0] << endl;
istringstream iss(phrase[0]);
cout << "Pig Latin phrase: ";
do {
string OGword;
string PLword;
for (int i=0; i < phrase.size(); i++){
iss >> OGword;
totalWords++;
}
if (OGword[0]=='a' || OGword[0]=='A' || OGword[0]=='e' || OGword[0]=='E' || OGword[0]=='i' || OGword[0]=='I' || OGword[0]=='o' || OGword[0]=='O' || OGword[0]=='u' || OGword[0]=='U'){
cout << OGword << "way" << " ";
totalLetters += (OGword.size() + 3);
}
else {
PLword = OGword.substr(index);
length = PLword.length();
PLword.insert(length, "ay");
PLword.insert(length, 1, OGword[index]);
PLword.erase(0, 1);
if (isupper(OGword[0])){
transform(PLword.begin(), PLword.end(), PLword.begin(), ::tolower);
(toupper(PLword[1]));
char upper;
upper = toupper(PLword[0]);
PLword.erase(0, 1);
cout << upper;
}
cout << PLword << " ";
totalLetters += PLword.size();
}
} while (iss);
totalChars = totalLetters + 1;
cout << "\n\nTotal words: " << totalWords << endl;
cout << "Total Letters: " << totalLetters << endl;
cout << "Total Characters: "<< totalChars << endl;
}
Problem
The core loop of the program looks like this (in pseudocode):
istringstream iss; // Contains line of text.
do {
string OGword;
get_OGword_and_count_totalWords(iss, OGword);
print_pig_latin_of_word(OGword);
} while (iss);
The loop runs as long as iss has not experienced an error. And in particular, iss does not experience an error until an extraction operation fails. So things happen in the loop like this:
OGword contains the last legitimate word on the line.
Print the last word.
The while clause is tested. iss is still good at this point because no error has occurred, even if iss is at the end of string.
Attempt to extract a word into OGword. This fails, and leaves OGword empty ("").
Print the Pig Latin version of "", which is "ay".
The while clause is tested. iss is in an error state, and the loop ends.
Fix
One possible fix out of many is to test iss for an error immediately after extracting a word.
std::istringstream iss; // Contains line of text
std::string OGword;
while (iss >> OGword) {
increment_word_total();
print_pig_latin_of_word(OGword);
}
In this version, the operation iss >> OGword returns iss, which is converted to bool. If there was an error during the immediately preceeding extraction, the loop ends without printing anything.
Other Advice
I think the best way to improve readability is to break the code up into smaller functions. For instance, take the if / else block that formats and prints the Pig Latin, and actually put it in a function:
int print_pig_latin_of_word_and_return_total_letters(string_view word);
Then, the code in that function can be further subdivided:
bool starts_with_vowel(std::string_view word);
int print_vowel_word_and_count_letters(std::string_view word);
int print_consonant_word_and_count_letters(std::string_view word);
int print_pig_latin_of_word_and_count_letters(std::string_view word) {
if (starts_with_vowel(word)) {
return print_vowel_word_and_count_letters(word);
} else {
return print_consonant_word_and_count_letters(word);
}
}
Odds and Ends
I would drop using namespace std and write all of the std library names as std::string, etc. This makes it clear which things are from the standard library.
The program has interesting behavior on input files that contain more than one line. There is a for loop that loops over phrase.size() which is number of input lines. This causes words to be skipped and totalWords to be incorrect.
This statement doesn't do anything, because the result of toupper is ignored:
(toupper(PLword[1]));
The following code is for a project I have to do where I recieve a text file that has a students first and last name followed by his grades. I then have to convert that into an output file that contains his name followed by his average score. The file I recieve has multiple students in it spereated line by line. The output should look relativly like
Rzam, Look = 0.00
Bambi, Lambi = 40.47
Coop, Jason = 27.31
but mine is merely printing garbage such as
0x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.000x7fffb08e8698= 0.00
Here is what I have so far:
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
using namespace std;
struct Student
{
string fname;
string lname;
double average;
};
int read(ifstream &fin, 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, size);
fin.close();
fout.close();
}
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;
}
}
You have a few bugs, which have added up to your issue:
in your print function, you write to a ostringstream and then try to write that to the file stream. Which is fine, but it is printing the address of the ostringstream buffer. So making this change will cause it to print the contents:
fout << sout.str() << setprecision(2) << fixed << "= " << s[i].average;
Note the usage of .str(). Though you don't really need a temporary stream here at all...
You don't place a newline in the output, so it all ends up one line making it hard to read:
so make another change making it look like this:
fout << sout.str() << setprecision(2) << fixed << "= " << s[i].average << '\n';
You need to place the ostringstream sout; inside the loop, so it is reset each time too. Otherwise you will get weirdly compounding output.
You don't use the count of students calculated by your read function! so it always tries to print 10! Do something like this:
int count = read(fin , s);
print(fout, s, count);
If no score is read, I think you'll have a divide by zero. So you should add a check.
You should ensure that no more than size Students are read. Or better yet, just place them in a std::vector and return that from the function. It's simpler and less error prone.
You need to reset i each time you start reading a student, or the later students will get divided by way too much. Each needs to have an independent count.
I don't know if these are the only issues, but certainly it should get you started on the right track :-)
I have the following structure:
struct productInfo
{
int item;
string details;
double cost;
};
I have a file that will input 10 different products that each contain an item, details, and cost. I have tried to input it using inFile.getline but it just doesn't work. Can anyone give me an example of how to do this? I would appreciate it.
Edit
The file contains 10 lines that look like this:
570314,SanDisk Sansa Clip 8 GB MP3 Player Black,55.99
Can you provide an example please.
Edit
Sorry guys, I am new to C++ and I don't really understand the suggestions. This is what I have tried.
void readFile(ifstream & inFile, productInfo products[])
{
inFile.ignore(LINE_LEN,'\n'); // The first line is not needed
for (int index = 0; index < 10; index++)
{
inFile.getline(products[index].item,SIZE,DELIMETER);
inFile.getline(products[index].details,SIZE,DELIMETER);
inFile.getline(products[index].cost,SIZE,DELIMETER);
}
}
This is another approach that uses fstream to read the file and getline() to read each line on the file. The parsing of the line itself was left out on purpose since other posts have already done that.
After each line is read and parsed into a productInfo, the application stores it on a vector, so all products could be accessed in memory.
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
struct productInfo
{
int item;
string details;
double cost;
};
int main()
{
vector<productInfo> product_list;
ifstream InFile("list.txt");
if (!InFile)
{
cerr << "Couldn´t open input file" << endl;
return -1;
}
string line;
while (getline(InFile, line))
{ // from here on, check the post: How to parse complex string with C++ ?
// https://stackoverflow.com/questions/2073054/how-to-parse-complex-string-with-c
// to know how to break the string using comma ',' as a token
cout << line << endl;
// productInfo new_product;
// new_product.item =
// new_product.details =
// new_product.cost =
// product_list.push_back(new_product);
}
// Loop the list printing each item
// for (int i = 0; i < product_list.size(); i++)
// cout << "Item #" << i << " number:" << product_list[i].item <<
// " details:" << product_list[i].details <<
// " cost:" << product_list[i].cost << endl;
}
EDIT: I decided to take a shot at parsing the line and wrote the code below. Some C++ folks might not like the strtok() method of handling things but there it is.
string line;
while (getline(InFile, line))
{
if (line.empty())
break;
//cout << "***** Parsing: " << line << " *****" << endl;
productInfo new_product;
// My favorite parsing method: strtok()
char *tmp = strtok(const_cast<char*>(line.c_str()), ",");
stringstream ss_item(tmp);
ss_item >> new_product.item;
//cout << "item: " << tmp << endl;
//cout << "item: " << new_product.item << endl;
tmp = strtok(NULL, ",");
new_product.details += tmp;
//cout << "details: " << tmp << endl;
//cout << "details: " << new_product.details << endl;
tmp = strtok(NULL, " ");
stringstream ss_cost(tmp);
ss_cost >> new_product.cost;
//cout << "cost: " << tmp << endl;
//cout << "cost: " << new_product.cost << endl;
product_list.push_back(new_product);
}
It depends on what's in the file? If it's text, you can use the redirect operator on a file input stream:
int i;
infile >> i;
If it's binary, you can just read it in to &your_struct.
You have to
0) Create a new instance of productInfo, pinfo;
1) read text (using getline) to the first comma (','), convert this string to an int, and put it into pinfo.item.
2) read text to the next comma and put it into pinfo.details;
3) read text to the endline, convert the string to a double, and put it into pinfo.cost.
Then just keep doing this until you reach the end of the file.
Here is how I would use getline. Note that I use it once to read from the input file, and then again to chop that line at ",".
ostream& operator>>(istream& is, productInfo& pi)
{
string line;
getline(is, line); // fetch one line of input
stringstream sline(line);
string item;
getline(sline, item, ',');
stringstream(item) >> pi.item; // convert string to int
getline(sline, item, ',');
pi.details = item; // string: no conversion necessary
getline(sline, item);
stringstream(item) >> pi.cost; // convert string to double
return is;
}
// usage:
// productInfo pi; ifstream inFile ("inputfile.txt"); inFile >> pi;
N.b.: This program is buggy if the input is
99999,"The Best Knife, Ever!",16.95