I'm creating a program that reads the author, title, and number of volumes from a file and prints out labels,
(ex.
Adams
A Complete History of the World
Volume 1 of 10
Adams
A Complete History of the World
Volume 2 of 10 etc.)
To make it read properly and not infinitely loop, I had to change all of my variable to string. However, for future reference to the volume number, I need it to be int so I can compare the amount.
My ideas for furthering the code with a do-while loop are commented in to show why I'd like vnum to have int value.
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
ifstream fin;
string author;
string title;
string vnum;
int counter=1;
fin.open("publish.txt", ios::in);
while (!fin.eof())
{
getline(fin, author);
getline(fin, title);
getline(fin, vnum);
//do
//{
cout << author << endl;
cout << title << endl;
cout << "Volume " << counter << " of " << vnum << endl;
cout << endl;
//counter++;
//} while(counter < vnum);
}
fin.close();
return 0;
}
File I'm reading from:
Adams
A Complete History of the World
10
Samuels
My Life of Crime
2
Baum
Wizard Stories
6
First of all, avoid use of
while (!fin.eof())
See Why is “while ( !feof (file) )” always wrong? to understand the problems it would cause.
Coming to your task, I would suggest:
Create a struct to hold the data.
Add a function to read all the members of the struct from a std::istream.
Add a function to write all the members of the struct to a std::ostream.
Simplify main to use the above.
Here's what I suggest:
struct Book
{
std::string author;
std::string title;
int volume;
};
std::istream& operator>>(std::istream& in, Book& book);
std::ostream& operator<<(std::ostream& out, Book const& book);
That will help simplify main to:
int main()
{
ifstream fin;
Book book;
// Not sure why you would need this anymore.
int counter=1;
fin.open("publish.txt", ios::in);
while ( fin >> book )
{
cout << book;
++counter;
}
return 0;
}
The functions to read and write a Book can be:
std::istream& operator>>(std::istream& in, Book& book)
{
// Read the author
getline(in, book.author);
// Read the title
getline(in. book.title);
// Read the volume
in >> book.volume;
// Ignore rest of the line.
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return in;
}
std::ostream& operator<<(std::ostream& out, Book const& book)
{
out << book.author << std::endl;
out << book.title << std::endl;
out << book.volume << std::endl;
return out;
}
Related
For a homework assignment, I am supposed to create a program that uses structures to hold an artists' discography information; i.e. name, start year, end year, genre(this can be more than one) and albums with their release year(which I interpreted to mean a second structure). All this information should be read from a file.
I'm having a little bit of trouble reading data from the file into the structure variables. My main issue is that a couple of my struct variables can hold multiple strings and I am confused as to how to read them into the variables as separate values. I've tried using 2D arrays, but that doesn't seem to work, so I think that vectors might be the way to go, but that seems to be throwing up issues as well. The file is formatted as such:
artist name
artist genres;....;...
start year
end year
number of albums
album name, album year; ..., ...;
So far this is the code that I have written:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Discography {
vector<string> trackTitle;
vector<int> trackYear;
};
struct Artist {
string artistName;
vector<string> genres;
int startingYear;
int endingYear;
int numOfTracks;
struct Discography disc;
};
istream& operator>>(istream& input, Artist& artist)
{
input >> artist.artistName;
//input >> artist.genres;
input >> artist.startingYear;
input >> artist.endingYear;
input >> artist.numOfTracks;
//input >> artist.disc.trackTitle;
//input >> artist.disc.trackYear;
return input;
}
void displayFileContents();
void searchByName();
void searchByGenre();
void searchBySong();
void searchByYear();
int main()
{
/*integer variable for menu selection*/
int selection;
vector<Artist> art;
do
{
cout << "\nPlease make a selection from one of"
<< "\nthe six options below.\n";
cout << "\n1. Display All Information\n";
cout << "2. Search by artist name\n";
cout << "3. Search by genre\n";
cout << "4. Search by song title\n";
cout << "5. Search by an active year\n";
cout << "6. Exit Program\n";
cout << "\nEnter your selection: ";
/*reading user menu selection*/
cin >> selection;
if (selection == 1) {
displayFileContents();
}
else if (selection == 2) {
searchByName();
}
else if (selection == 3) {
searchByGenre();
}
else if (selection == 4) {
searchBySong();
}
else if (selection == 5) {
searchByYear();
}
else if (selection == 6) {
cout << "Good Bye!";
}
else {
cout << "You did not enter a valid selection\n\n";
}
} while (selection != 6);
return 0;
}
void displayFileContents() {
ifstream artistFile;
artistFile.open("My Artists.txt");
Artist a[5];
if (!artistFile.is_open()) {
cout << "File could not open";
}
else
while (std::getline(artistFile, a->artistName)) {
std::cout << a->artistName << "\n";
}
}
void searchByName() {
}
void searchByGenre() {
}
void searchBySong() {
}
void searchByYear() {
}
The ultimate goal is to display the information and also to be able to search the info based on certain things like the year or artist name or genre. That part of the program I feel comfortable with, but the file reading is really stumping me. A nudge in the right direction would greatly appreciated. Thanks in advance.
I think your approach is good so far. In the struct Discography maybe you could use std::unordered_map<int, std::string> instead, but that's just my opinion. If you know in advance the number of input lines your data is composed of, I strongly recommend changing your istream& operator>>(istream& input, Artist& artist) to use std::getline:
std::istream& operator>>(std::istream& input, Artist& artist)
{
std::string line;
std::getline(input, line);
artist.artistName = line;
std::getline(input, line);
auto genres = splitString(line, ';');
artist.genres = genres;
std::getline(input, line);
artist.startingYear = std::stoi(line);
// etc ...
return input;
}
To implement std::vector<std::string> splitString(const std::string& str, char delim); you can use this answer. Then you can also think of all kinds of error handling like invalid integer conversion, empty line, not enough lines, etc.
I trying to write a function which searches for an ID and prints the book name and author name. I have been able to match the ID, however not able to properly print the book and author name. Text file is stored as such:
ID
book name
author name
Following is the code for my search function:
void searching() {
string search, id, name;
ifstream myfile("books.txt");
bool found = false;
string line;
cout << "\nEnter ID to search : ";
cin >> search;
int srchlen = search.length();
if(myfile.is_open()) {
while(getline(myfile, line)) {
id = line.substr(0, srchlen);
if(id == search) {
found = true;
break;
} else {
found = false;
}
}
if(found == true) {
name = line;
cout << "ID\tNAME\tAUTHOR\n";
cout << name;
} else {
cout << "ID doesnt exist";
}
}
}
Here is how the text file looks like (there is a blank line between each book):
98
crime and punishment
Dostoevsky
70
The Da Vinci Code
Dan Brown
So there's a logical flaw in your code that makes it harder to do what you want. The book data is stored on three separate lines, but your code reads one line at a time. Logically you should be reading three lines at a time. By doing this you'll have all the information available for one book at the same time.
Like this
string id, title, author;
while (getline(myfile, id) && getline(myfile, title) && getline(myfile, author)) {
string blank;
getline(myfile, blank); // skip the blank line between books
id = id.substr(0, srchlen);
if (id == search) {
found = true;
break;
} else {
found = false;
}
}
if (found == true) {
cout << "ID\tNAME\tAUTHOR\n";
cout << id << ' ' << title << ' ' << author << '\n';;
} else {
cout << "ID doesnt exist";
}
Note that reading the blank line is not part of the while condition. We don't want not to consider a book just because it wasn't followed by a blank line. This might happen at the end of the file for example.
To make it easier to deal with the data, I recommend putting the information about a book into a class (struct) and to add operators for reading/writing one book at a time from/to an istream/ostream.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>
struct Book {
std::string id;
std::string title;
std::string author;
};
// operator for reading one book title from any istream
std::istream& operator>>(std::istream& is, Book& b) {
std::getline(is, b.id);
std::getline(is, b.title);
std::getline(is, b.author);
// ignore the blank line between books in the file:
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return is;
}
// operator for writing one book title to any ostream
std::ostream& operator<<(std::ostream& os, const Book& b) {
return os << b.id << '\n' << b.title << '\n' << b.author << "\n\n";
}
int main() {
std::string search;
std::cout << "\nEnter ID to search : ";
std::cin >> search;
if(std::ifstream myfile("books.txt"); myfile) {
Book book;
// read one book at a time from the stream using the added
// operator>>
while(myfile >> book) {
if(book.id == search) {
// book found, write it to cout using the added
// operator<<
std::cout << book;
break;
}
}
}
}
By taking some advantages of C++ such as vector, you can make the code easier:
#include <iostream>
#include <vector>
#include <fstream>
const char *fileName = "books.txt";
// structure of the file
struct book {
int ID;
std::string author;
std::string bookName;
};
int main(void) {
std::ifstream file(fileName); // to read the file
std::vector<book> vec; // to find the content required
int ID; // the ID to find
book temp; // temporary 'book' to insert in vector
bool found = false;
// if the file was unable to open
if (!file.is_open()) {
std::cout << "Unable to open the file." << std::endl;
return -1;
}
// getting the content of the file
while (file >> temp.ID >> temp.bookName >> temp.author)
vec.push_back(temp);
std::cout << "Enter the ID to find: ";
std::cin >> ID;
// matching the IDs
for (size_t i = 0, len = vec.size(); i < len; i++)
// prints when ID asked to show is present in the file
if (vec[i].ID == ID) {
std::cout << "ID: " << vec[i].ID << " | Author: "
<< vec[i].author << " | Name: " << vec[i].bookName;
found = true;
break;
}
if (!found)
std::cout << "The ID does not exist." << std::endl;
return 0;
}
Supposing the books.txt contains:
// FORMAT: ID _ Book _ Author
10 BookABC John
20 BookXYZ Ben
30 CodingBook Rock
Then the output would be something like:
Enter the ID to find: 30
ID: 30 | Author: Rock | Name: CodingBook
I'm working on this program that is to help manage a DVD rental store. What I have to do is take a text that contains info about DVD etc:
Mean girls; comedy; PG; 2009; Regina George; 12.07.2015;
The Conjuring; Horror; R; 2013; Sara Johnson; 16.05.2016;
Pokemon 2000; Kids; G; 2000; Ash Katchem; 15.04.2016;
etc..
And then takes this information and then reads it into an array and from there the array is read into the struct and then displayed in proper order like so:
Name: Mean Girls
Genre: Comedy
Rating: PG
etc...
This is my code so far:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
struct dvd{
string name;
string genre;
string rating;
string released;
string renter;
string rentday;
void print();
bool read(ifstream & file);
};
void dvd::print(){
cout <<"Title: " << name << endl;
cout <<"Genre: " << genre << endl;
cout << "Rating: " << rating << endl;
cout << "Release date: " << released << endl;
cout << "Name of renter: " << renter << endl;
cout << "Date rented: " << rentday << endl;
}
bool dvd::read(ifstream & file)
{
getline(file, name, ';');
getline(file, genre, ';');
getline(file, rating,';');
getline(file, released, ';');
getline(file, renter, ';');
getline(file, rentday, ';');
return file.good();
}
int main() {
vector<dvd> dvds;
dvd dvd1;
ifstream file("DVD.txt");
if(! file.is_open()){
cout << "Failed to find input file" << endl;
return 1;
}
while(dvd1.read(file))
{ dvds.push_back(dvd1);
}
dvd1.print();
return 0;
}
So what I would like to do is have read the text file into the Array and from there read the Array into the struct. So instead of the text file reading into the vector I need it to read into the array and from there read the first line of the array (dvdArray[1]) into struct dvd and then print out that information using print function and then loop that until dvdArray[10] is read into struct dvd!
Thank you so much for your help! :)
Change
bool dvd::read(ifstream & file)
to
bool dvd::read(istream & file)
No other changes to its contents are required.
Then, take each line and put it into a std::istringstream, then pass it to dvd::read.
You should be able to figure out the rest on your own.
For simple reading from and writing to file, I would suggest overload << and >> for your struct class, in order to make the code easy to serialize and de-serialize in a readable fashion.
friend std::ostream& operator<< (std::ostream& stream, const dvd& dvdObj)
{
// your output stuff
// stream <<"Title: " << dvdObj.name << endl;
// ...
return stream;
}
friend std::istream& operator>> (std::istream& stream, dvd& dvdObj)
{
// your output stuff
// getline(stream, dvdObj.name, ';');
// ...
return stream;
}
Then,
// look for std::copy for reading directly into vector ... else
while( file >> dvd1 )
{
dvds.push_back(dvd1);
}
And,
for( const auto& dvd1: dvds )
{
std::cout << dvd1 ;
}
I am new to programming and I am having trouble reading data from a file and entering it into a struct array, while keeping track of each data being entered:
The file would contain:
Name, ID Number, and GPA
Courtney Love 1234569 3.5
Bob Joe 1234570 3.0
Dave Henry 1234571 2.9
struct Student
{
string name;
int id;
float GPA;
void printStudent();
};
Declare an array of Student type that can hold up to 5 members:
Student a_members[5];
Open the file, read in each line and store the data in the array, keep track of each student read in:
fstream file_;
file_.open ("students.txt");
if(file_.is_open())
{
while(file_.good())
{
}
}
else
{
cout << "File is not open"<< endl;
}
return 0;
I am stuck on the "while" conditional statement. After that I don't know what I should do to input the data from the file line by line and place into the "struct array". As of right now, I feel like I have tried everything! I deleted everything and figured it was best to start over. It was becoming too complicated! Maybe I am just not understanding the concept. If anyone can point me in the right direction, please do so! Thank you!
You should not use good(), just like you should not use eof().
(Neither is used in any decent beginner-level material, yet every beginner manages to find them. And then they wonder why it didn't work.)
You should instead rely on the fact that a stream itself is "true-ish" if it's in a good state, and just keep reading until it isn't.
Idiomatic C++ would look like this:
std::ifstream file("students.txt");
Student s;
while (file >> s.name >> s.id >> s.GPA)
{
// Process the student
}
or, a fancy version:
std::istream& operator>> (std::istream& is, Student& s)
{
return is >> s.name >> s.id >> s.GPA;
}
std::ifstream file("students.txt");
Student s;
while (file >> s)
{
// Process the student
}
(In your code, you'll need to also keep track of how many Students you've read.)
Here is one from possible solutions:
#include <iostream>
#include <vector>
#include <fstream>
struct Student
{
Student() : first_name(), surname(){}
char first_name[64];
char surname[64];
int id;
float GPA;
void printStudent()
{
std::cout << "Name: " << first_name << " " << surname << " ID: " << id << " GPA: " << this->GPA << std::endl;
}
};
std::vector<Student>student;
bool LoadFile(const char* filename)
{
if (filename == NULL)return false;
std::fstream stream(filename, std::ios::in);
if (!stream.is_open())return false;
else
{
char buffer[255]; // for solution 1!
while (!stream.eof())
{
memset(buffer, 0, sizeof(buffer));
Student _student;
#pragma region SOLUTION_1
//stream.getline(buffer, sizeof(buffer));
//sscanf(buffer, "%s %s %d %f", _student.first_name, _student.surname, &_student.id, &_student.GPA);
#pragma endregion
#pragma region SOLUTION_2
stream >> _student.first_name >> _student.surname >> _student.id >>_student.GPA;
#pragma endregion
student.push_back(_student);
student[student.size() - 1].printStudent();
}
}
return true;
}
int main()
{
LoadFile("students.txt");
getchar();
return 0;
}
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.