This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 1 year ago.
I am trying to read data from a file to an array of struct, and then print it.
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
void getmatches() {
int i = 0;
const int size = 50;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
match getmatches[100];
fstream show;
show.open("matches.txt");
while (! show.eof () && i < 10 )
{
getline(show, getmatches[i].id);
getline(show, getmatches[i].team1);
getline(show, getmatches[i].team2);
getline(show, getmatches[i].date);
getline(show, getmatches[i].time);
getline(show, getmatches[i].league);
cout << "the match id: " << getmatches[i].id << endl;
cout << "teams " << getmatches[i].team1 << "vs" << getmatches[i].team2 << endl;
cout << "time of match:" << getmatches[i].time << endl;
cout << "date of match " << getmatches[i].date << endl;
cout << "league:" << getmatches[i].league << endl << endl << endl;
i++;
}
return show.close();
}
int main() {
getmatches();
}
The file I am dealing with has each element in a line, and the problem is that it gives me an extra empty output. I tried to change the counter and condition, but it didn't work.
There is nothing more I can think of. I think it's a basic solution, but I can't figure it out.
Don't use eof() in a loop, the way you are. Define an operator>> for match instead.
There are other issues with your code, too. You have struct match defined twice. Your getmatches[] array has the same name as the function it belongs to, use a more unique name instead. And getmatches() has a void return value, but you are trying to return a (void) value, which is not necessary.
Try this instead:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
istream& operator>>(istream &in, match &m) {
getline(in, m.id);
getline(in, m.team1);
getline(in, m.team2);
getline(in, m.date);
getline(in, m.time);
getline(in, m.league);
return in;
}
ostream& operator<<(ostream &out, const match &m) {
out << "the match id: " << m.id << "\n";
out << "teams: " << m.team1 << " vs " << m.team2 << "\n";
out << "time of match: " << m.time << "\n";
out << "date of match: " << m.date << "\n";
out << "league: " << m.league << "\n\n";
return out;
}
void getmatches() {
int i = 0;
const int size = 50;
match matches[size];
ifstream show("matches.txt");
while (i < size && show >> matches[i])
{
cout << matches[i] << endl;
++i;
}
}
int main() {
getmatches();
}
Demo
Related
trying to format with c++ getline function. The output puts everything at the first record number forename instead of where it should go.
Code:
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{
const int RANGE = 12;
string tab[RANGE];
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
while (!reader.eof())
{
if ( ( i + 1) % 4 == 0)
getline( reader, tab[i++], '\n');
else
getline( reader, tab[i++], '\t');
}
reader.close();
i = 0;
while (i < RANGE)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
Contents of TXT file:
John Smith Sales 555-1234
Mary Jones Wages 555-9876
Paul Harris Accts 555-4321
Please run the code for yourself to understand what happens and put the txt file in the same folder as your code.
Hope someone can help me thanks.
See Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?.
Also, your final while loop should only output the strings that were actually read into the array, not the full array, if the file has less than 12 strings. But unless you can guarantee that your file never exceeds 12 strings, you should use std::vector instead of a fixed array.
Also, instead of alternating the getline() delimiter in a single loop, I would just use an outer loop to read whole lines only, and then separately read tab-delimited values from each line. And then store the values in an array/vector of struct instead of individually.
Try something more like this:
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
struct Person
{
string foreName;
string surName;
string department;
string phoneNumber;
};
int main()
{
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
vector<Person> people;
string line;
while (getline(reader, line))
{
istringstream iss(line);
Person p;
getline(iss, p.foreName, '\t');
getline(iss, p.surName, '\t');
getline(iss, p.department, '\t');
getline(iss, p.phoneNumber, '\t');
people.push_back(p);
}
reader.close();
int j = 0;
for (Person &p : people)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << p.foreName << endl;
cout << "Surname: " << p.surName << endl;
cout << "Department: " << p.department << endl;
cout << "Telephone: " << p.phoneNumber << endl;
}
return 0;
}
There are easier ways to separate words in an istream, namely C++ sring stream tools:
#include <fstream>
#include <iostream>
#include <sstream> //<-- string stream library
using namespace std; //<-- should not be used, use scope std::
int main() {
const int RANGE = 12;
string tab[RANGE];
string temp; //<--to store each field temporarily
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader) {
cout << "Error opening input file" << endl;
return -1;
}
while (getline(reader, temp)) { //<-- read one full line
stringstream ss(temp); // <-- input to a string stream
while(ss >> tab[i]){ // <-- passing strings to the string array one by one
i++;
}
}
reader.close();
i = 0;
while (i < RANGE) {
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
The idea here was to mess as little as possible with your code, one thing I would advise is to use std::vector instead of normal fixed size arrays. Also, as it was said and linked, eof is very unreliable.
The source of your problem, I think, is explained in Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?.
You are reading into tab[12], tab[13], tab[13], and tab[14] due to that error. Of course, that leads to undefined behavior.
Change the loop to:
// Read the contents of the file line by line
std::string line;
while (getline( reader, line))
{
// Process each line's contents.
std::istringstream str(line);
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\n');
}
Make sure to add
#include <sstream>
To be doubly sure that you are not using the array using out of bounds indices, add a check.
while ( i+4 < RANGE && getline( reader, line))
{
...
}
First, while (!reader.eof()) is not doing the right thing.
The immediate problem you see is caused by the fact that your file does not contain '\t', hence already the very first getline reads all the contents of the file into tab[0]. (At least thats what I got after 1-to-1 copying your file contents)
Your code is rather difficult, because you declare variables long before you use them and later reuse them. You have a fixed size array, but when there are more lines in the file your code will just crash. Also reading everything into a plain array of strings is making things complicated. Accessing forename or other fields requires you to compute the offset into the array. Better use a data structure:
struct file_entry {
std::string first_name;
std::string last_name;
std::string departure;
std::string phone;
};
Then you can define an input operator:
std::istream& operator>>(std::istream& in,file_entry& fe) {
return in >> fe.first_name >> fe.last_name >> fe.departure >> fe.phone;
};
And use a std::vector to store as many entries as there are in the file:
int main() {
std::string contents{"John Smith Sales 555-1234\n"
"Mary Jones Wages 555-9876\n"
"Paul Harris Accts 555-4321\n"};
std::stringstream reader{contents};
std::vector<file_entry> data;
std::string line;
while (std::getline(reader,line)) {
file_entry fe;
std::stringstream{line} >> fe;
data.push_back(fe);
}
for (const auto& fe : data) {
std::cout << "Forename: " << fe.first_name << '\n';
std::cout << "Surname: " << fe.last_name << '\n';
std::cout << "Department: " << fe.departure << '\n';
std::cout << "Telephone: " << fe.phone << '\n';
}
}
live example
PS you do not need to call close on the file, this is already done in its destructor. Not calling it explicitly has the benefit that the same code that works for a file stream also works for a stringstream.
the program should read from 2 files (author.dat and citation.dat) and save them into a map and set;
first it reads the citationlist without problem, then it seems to properly read the authors and after it went through the whole list (author.dat) a floating point exception arises .. can't quite figure out why
seems to happen in author.cpp inside the constructor for authorlist
author.cpp:
#include <fstream>
#include <iostream>
#include "authors.h"
using namespace std;
AuthorList::AuthorList(char *fileName) {
ifstream s (fileName);
int idTemp;
int nrTemp;
string nameTemp;
try {
while (true){
s >> idTemp >> nrTemp >> nameTemp;
cout << idTemp << " " << nrTemp << " " << nameTemp << " test_string";
authors.insert(std::make_pair(idTemp,Author(idTemp,nrTemp,nameTemp)));
if (!s){
cout << "IF-CLAUSE";
throw EOFException();
}
cout << "WHILE-LOOP_END" << endl;
}
} catch (EOFException){}
}
author.h:
#ifndef CPP_AUTHORS_H
#define CPP_AUTHORS_H
#include <iostream>
#include <map>
#include <string>
#include "citations.h"
class Author {
public:
Author (int id, int nr, std::string name) :
articleID(id),
authorNR(nr),
authorName(name){}
int getArticleID() const {
return articleID;
}
std::string getAuthorName() const {
return authorName;
}
private:
int articleID;
int authorNR;
std::string authorName;
};
class AuthorList {
public:
AuthorList(char *fileName);
std::pair<std::multimap<int,Author>::const_iterator, std::multimap<int,Author>::const_iterator> findAuthors(int articleID) {
return authors.equal_range(articleID);
}
private:
std::multimap<int,Author> authors;
};
#endif //CPP_AUTHORS_H
programm.cpp:
#include <iostream>
#include <cstdlib>
#include "citations.h"
#include "authors.h"
#include "authorCitation.h"
using namespace std;
int main(int argc, char *argv[]){
CitationList *cl;
AuthorList *al;
//check if argv array has its supposed length
if (argc != 4){
cerr << "usage: programm article.dat citation.dat author.dat";
return EXIT_FAILURE;
}
//inserting citation.dat and author.dat in corresponding lists (article.dat not used)
cl = new CitationList(argv[2]);
al = new AuthorList(argv[3]);
try {
AuthorCitationList *acl;
acl->createAuthorCitationList(al,cl);
acl->printAuthorCitationList2File("authorcitation.dat");
} catch (EOFException){
cerr << "something went wrong while writing to file";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
All files:
https://drive.google.com/file/d/0B734gx5Q_mVAV0xWRG1KX0JuYW8/view?usp=sharing
I am willing to bet that the problem is caused by the following lines of code:
AuthorCitationList *acl;
acl->createAuthorCitationList(al,cl);
You are calling a member function using an uninitialized pointer. I suggest changing the first line to:
AuthorCitationList *acl = new AuthorCitationList;
Add any necessary arguments to the constructor.
While you are at it, change the loop for reading the data also. You have:
while (true){
s >> idTemp >> nrTemp >> nameTemp;
cout << idTemp << " " << nrTemp << " " << nameTemp << " test_string";
authors.insert(std::make_pair(idTemp,Author(idTemp,nrTemp,nameTemp)));
if (!s){
cout << "IF-CLAUSE";
throw EOFException();
}
cout << "WHILE-LOOP_END" << endl;
}
When you do that, you end up adding data once after the end of line has been reached. Also, you seem to have the last line in the wrong place. It seems to me that it should be outside the while loop.
You can use:
while (true){
s >> idTemp >> nrTemp >> nameTemp;
// Break out of the loop when reading the
// data is not successful.
if (!s){
cout << "IF-CLAUSE";
throw EOFException();
}
cout << idTemp << " " << nrTemp << " " << nameTemp << " test_string";
authors.insert(std::make_pair(idTemp,Author(idTemp,nrTemp,nameTemp)));
}
cout << "WHILE-LOOP_END" << endl;
You can simplify it further by using:
while (s >> idTemp >> nrTemp >> nameTemp){
cout << idTemp << " " << nrTemp << " " << nameTemp << " test_string";
authors.insert(std::make_pair(idTemp,Author(idTemp,nrTemp,nameTemp)));
}
cout << "WHILE-LOOP_END" << endl;
I'm completely new to C++ and currently I'm trying to read very basic text file which look like this:
Dr John Doe
British
2
Soccer
Swimming
and my expected output should look like:
My information
Name: John Doe
Nationality: British
I have 2 hobbies:
1. Soccer
2. Swimming
My header file:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
const int MAX = 80;
const int MAXNO = 5;
enum Title {Miss, Mrs, Mr, Dr, Unknown};
struct Date
{
int day;
int month;
int year;
};
struct MyInfo
{
char name [MAX];
char national [MAX];
int noOfHobbies;
char hobby [MAXNO][MAX];
};
void getMyInfo (fstream& , char[] , MyInfo&);
void displayMyInfo (MyInfo);
My functions:
#include "Lab_1.h"
void getMyInfo (fstream& afile,char fileName[], MyInfo& x) {
afile.open (fileName);
if (!afile)
{
cout << "Binary file " << fileName << " opened for creation failed" << endl;
exit (-1);
}
cout << "\n" << "Begin reading of " << fileName << endl;
string line;
while(getline(afile, line))
{
afile >> x.national;
afile >> x.noOfHobbies;*/
if (afile >> x.name >> x.national >> x.noOfHobbies) {
cout << "Name: " << x.name << ", "
<< "National: " << x.national << ", "
<< "noOfHobbies: " << x.noOfHobbies << ", "
<< endl;
}
}
}
void displayMyInfo (MyInfo x) {
}
My main function:
#include "Lab_1.h"
int main () {
fstream afile;
MyInfo x;
string fileName;
getMyInfo(afile,"textfile.txt",x);
//displayMyInfo(x);
afile.close ();
}
The above code output nothing because I just put everything I understand over the forum with similar question. Since I'm already stuck for 1 day even though I've already done a lot of research but most of them suggest to use vector which I'm not familiar with at this moment, so can someone give me a solution to this problem? Thank you very much for your help in advance.
Random act of madness kindness:
Live On Coliru
#include <fstream>
#include <set>
struct Person {
std::string name;
std::string nationality;
std::set<std::string> hobbies;
friend std::istream& operator>>(std::istream& is, Person& into) {
size_t n = 0;
if (getline(is, into.name) &&
getline(is, into.nationality) &&
is >> n && is.ignore(1024, '\n'))
{
while (n--) {
std::string hobby;
if (getline(is, hobby))
into.hobbies.insert(hobby);
else
is.setstate(std::ios::failbit);
}
}
return is;
}
};
#include <iostream>
int main() {
std::ifstream ifs("input.txt");
Person p;
if (ifs >> p) {
std::cout << "My information\n";
std::cout << p.name << "\n";
std::cout << p.nationality << "\n";
std::cout << "I have " << p.hobbies.size() << " hobbies:\n";
size_t counter = 0;
for(auto const& hobby : p.hobbies) {
std::cout << ++counter << ". " << hobby << "\n";
}
} else {
std::cerr << "Parse failure\n";
}
}
I've almost finished writing a program that will detect palindromes from a file and output a new file highlighting the palindromes but I'm stuck on a really dumb error. I'm trying to write a test for one of my methods (TDD) and, for some reason, it's not recognizing the function as within the scope.
I'm calling the isPalindrome(string s) method (declared in PalindromeDetector.h) in my isPalindromeTest() method (declared in PalindromeDetectorTest.h) but, for some reason, it's not recognizing it as within the scoope.
I feel like everything should be working but it just isn't. Any help you can provide would be greatly appreciated. Below is my code:
PalindromeDetector.h
#ifndef PALINDROMEDETECTOR_H_
#define PALINDROMEDETECTOR_H_
#include <iostream>
using namespace std;
class PalindromeDetector {
public:
void detectPalindromes();
bool isPalindrome(string s);
};
#endif /* PALINDROMEDETECTOR_H_ */
PalindromeDetector.cpp
#include "PalindromeDetector.h"
#include "Stack.h"
#include "ArrayQueue.h"
#include <iostream>
#include <fstream>
#include <cassert>
#include <cctype>
#include <string>
using namespace std;
void PalindromeDetector::detectPalindromes() {
cout << "Enter the name of the file whose palindromes you would like to detect:" << flush;
string fileName;
cin >> fileName;
cout << "Enter the name of the file you would like to write the results to: " << flush;
string outFileName;
cin >> outFileName;
fstream in;
in.open(fileName.c_str());
assert(in.is_open());
ofstream out;
out.open(outFileName.c_str());
assert(out.is_open());
string line;
while(in.good()){
getline(in, line);
line = line.erase(line.length()-1);
if(line.find_first_not_of(" \t\v\r\n")){
string blankLine = line + "\n";
out << blankLine;
} else if(isPalindrome(line)){
string palindromeYes = line + " ***\n";
out << palindromeYes;
} else {
string palindromeNo = line + "\n";
out << palindromeNo;
}
if(in.eof()){
break;
}
}
in.close();
out.close();
}
bool PalindromeDetector::isPalindrome(string s){
unsigned i = 0;
Stack<char> s1(1);
ArrayQueue<char> q1(1);
while(s[i]){
char c = tolower(s[i]);
if(isalnum(c)){
try{
s1.push(c);
q1.append(c);
} catch(StackException& se) {
unsigned capS = s1.getCapacity();
unsigned capQ = q1.getCapacity();
s1.setCapacity(2*capS);
q1.setCapacity(2*capQ);
s1.push(c);
q1.append(c);
}
}
i++;
}
while(s1.getSize() != 0){
char ch1 = s1.pop();
char ch2 = q1.remove();
if(ch1 != ch2){
return false;
}
}
return true;
}
PalindromeDetectorTest.h
#ifndef PALINDROMEDETECTORTEST_H_
#define PALINDROMEDETECTORTEST_H_
#include "PalindromeDetector.h"
class PalindromeDetectorTest {
public:
void runTests();
void detectPalindromesTest();
void isPalindromeTest();
};
#endif /* PALINDROMEDETECTORTEST_H_ */
PalindromeDetectorTest.cpp
#include "PalindromeDetectorTest.h"
#include <cassert>
#include <iostream>
#include <fstream>
#include <cctype>
#include <string>
using namespace std;
void PalindromeDetectorTest::runTests(){
cout << "Testing palindrome methods... " << endl;
detectPalindromesTest();
isPalindromeTest();
cout << "All tests passed!\n" << endl;
}
void PalindromeDetectorTest::detectPalindromesTest(){
cout << "- testing detectPalindromes()... " << flush;
fstream in;
string fileName = "testFile.txt";
in.open(fileName.c_str());
assert(in.is_open());
cout << " 1 " << flush;
ofstream out;
string fileOutName = "testFileOut.txt";
out.open(fileOutName.c_str());
assert(out.is_open());
cout << " 2 " << flush;
cout << " Passed!" << endl;
}
void PalindromeDetectorTest::isPalindromeTest(){
cout << "- testing isPalindrome()... " << flush;
// test with one word palindrome
string s1 = "racecar";
assert(isPalindrome(s1) == true); // these are not recognized within the scope
cout << " 1 " << flush;
// test with one word non-palindrome
string s2 = "hello";
assert(isPalindrome(s2) == false); // these are not recognized within the scope
cout << " 2 " << flush;
// test with sentence palindrome
string s3 = "O gnats, tango!";
assert(isPalindrome(s3) == true); // these are not recognized within the scope
cout << " 3 " << flush;
// test with sentence non-palindrome
string s4 = "This is not a palindrome.";
assert(isPalindrome(s4) == false); // these are not recognized within the scope
cout << " 4 " << flush;
cout << " Passed!" << endl;
}
isPalindrome is a member function of PalindromeDetector, but you are trying to call it from within a PalindromeDetectorTest method. If the test class derived from PalindromeDetector this would work, but there isn't (and almost certainly shouldn't be) any such relationship between them.
You need a PalindromeDetector object to call the method on. Probably just as simple as this:
void PalindromeDetectorTest::isPalindromeTest(){
cout << "- testing isPalindrome()... " << flush;
PalindromeDetector sut; // "subject under test"
// test with one word palindrome
string s1 = "racecar";
assert(sut.isPalindrome(s1) == true);
// etc.
}
You could also make the PalindromeDetector methods static since the object doesn't appear to have any state. Then you could simply call PalindromeDetector::isPalindrome(s1); without the need to create an instance.
Say I have a structure as follows
struct stock{
string ticker;
double price;
double volume;
double eps;
};
If I want to output one of the variables such as price when asked for it would I have to do a large if/else or switch statement to match up the user input with the member or is there a more elegant way to do it because I know stock.userInput does not work.
there's no special keyword to find your variable(sorry to burst your bubble), You would have to use a logical statement. It would go along:
cout << "What would you like to see? (1)Price (2)etc...etc...";
cin >> input;
switch(input)
{
case 1:
cout << Obj.Price;
break;
case 2:
cout << //....
break;
}
I personally like using keys and a switch statement, it tends to be a lot cleaner and easier to go back and modify later in the program.
struct stock s1;
cout<<" price is:"<< s1.price;
If you want to get rid of the large switch/if statement, you can use map with string and a pointer-to-member. Assuming your stock struct, you can use:
Define the map (for doubles here) and initialize it:
std::map<std::string,double stock::*> double_members;
double_members["price"]=&stock::price;
double_members["volume"]=&stock::volume;
double_members["eps"]=&stock::eps;
And use it to look up some values:
stock stock1;
std::string input;
std::cin >> input;
if (double_members.find(input)!=double_members.end())
std::cerr << "Value for " << input << " is: " << stock1.*double_members[input] << std::endl;
else
std::cerr << "There's no field called " << input << std::endl;
It's limited to a single type, but you can't have a statement like std::cerr << A; and have A's type resolved during runtime. If you care only about string (or any other, but always the same) representation of the values, then you can wrap maps for different types in a class that searches all of them and outputs the value converted to a string (or something).
But it's probably easier to have the if statement, unless the struct is really big.
If it's okay with you that it doesn't work with g++ 4.7.1 and earlier (but does work with Visual C++ 11.0 and later), then like …
#include <sstream> // std::ostringstream
#include <string> // std::string
using namespace std;
struct Stock
{
string ticker;
double price;
double volume;
double eps;
string toString() const
{
ostringstream stream;
stream
<< "Stock("
<< "ticker='" << ticker << "', "
<< "price=" << price << ", "
<< "volume=" << volume << ", "
<< "eps=" << eps
<< ")";
return stream.str();
}
Stock(): ticker(), price(), volume(), eps() {}
};
#include <iostream>
#include <regex>
#include <stdexcept>
#include <stdlib.h>
bool err( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
string lineFromUser( string const& prompt )
{
string line;
cout << prompt;
getline( cin, line )
|| err( "oh my, failed to read line of input" );
return line;
}
void cppMain()
{
Stock stock;
stock.price = 1.23;
string const s = stock.toString();
cout << s << endl;
string const fieldname = lineFromUser( "Which field u want? " );
regex const valuespec( fieldname + "\\s*\\=\\s*([^,\\)]*)" ); //
smatch what;
if( regex_search( s, what, valuespec ) )
{
cout << "As we all know already, " << what.str() << "." << endl;
}
else
{
cout
<< "!Sorry, there's no field named '"
<< fieldname << "'"
<< endl;
}
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
Example usage:
[d:\dev\test]
> foo
Stock(ticker='', price=1.23, volume=0, eps=0)
Which field u want? eps
As we all know already, eps=0.
[d:\dev\test]
> _