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
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'd like to show the employee number name, occupation, and department of employees from a text file called organisation.txt, and save them in the variables declared in the class OrganisationRecord.
How can I do that?
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#define ORGANISATIONALRECORDSFILE "organisation.txt"
#define HRRECORDSFILE "HR_records.txt"
#define PAYROLLRECORDSFILE "payroll_records.txt"
using namespace std;
class OrganisationRecord
{
private:
public:
string name;
string occupation;
string department;
};
class HRRecord
{
private:
public:
string address;
string phonenumber;
string ninumber;
};
class PayrollRecord
{
private:
public:
string ninumber;
double salary;
};
class PayrollProcessing
{
private:
ifstream inputfile;
ofstream outputfile;
vector<OrganisationRecord> OrganisationRecords;
vector<HRRecord> HRRecords;
vector<PayrollRecord> PayrollRecords;
public:
void loadOrganisationRecords(string filename);
void loadHRRecords(string filename);
void loadPayrollRecords(string filename);
void displayEmployeeOfSalaryGTE(double salary);
//GTE = greater than or equal to
};
void PayrollProcessing::loadOrganisationRecords(string filename)
{
inputfile.open(ORGANISATIONALRECORDSFILE);
if (!inputfile)
{
cout << "the organisation records file does not exist" << endl;
return;
}
OrganisationRecord _organisationrecord;
int employeenumber;
while (inputfile >> employeenumber)
{
while (inputfile >> _organisationrecord.name)
{
cout << _organisationrecord.name;
cout << _organisationrecord.occupation;
cout << _organisationrecord.department <<endl;
}
OrganisationRecords.push_back(_organisationrecord);
}
}
int main(void)
{
PayrollProcessing database1;
database1.loadOrganisationRecords(ORGANISATIONALRECORDSFILE);
return 0;
}
organisation.txt
0001
Stephen Jones
Sales Clerk
Sales
0002
John Smith
Programmer
OS Development
0003
Fred Blogs
Project Manager
Outsourcing
When you use inputfile >> _organisationrecord.name it stops at the first whitespace character. Hence, only Stephen will be read and stored in organisationrecord.name.
You need to change your strategy a bit.
Read the contents of the file line by line. Stop when there are no more lines left.
Process each line as you seem fit.
Here's one way to deal with the input.
std::string line;
while ( std::getline(inputfile, line) )
{
// Extract the employeenumber from the line
std::istringstream str(line);
if ( !(str >> employeenumber) )
{
// Problem reading the employeenumber.
// Stop reading.
break;
}
if (!std::getline(inputfile, line) )
{
// Problem reading the next line.
// Stop reading.
break;
}
_organisationrecord.name = line;
if (!std::getline(inputfile, line) )
{
// Problem reading the next line.
// Stop reading.
break;
}
_organisationrecord.occupation = line;
if (!std::getline(inputfile, line) )
{
// Problem reading the next line.
// Stop reading.
break;
}
_organisationrecord.department = line;
std::cout << _organisationrecord.employeenumber << std::endl;
std::cout << _organisationrecord.name << std::endl;
std::cout << _organisationrecord.occupation << std::endl;
std::cout << _organisationrecord.department << endl;
OrganisationRecords.push_back(_organisationrecord);
}
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'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 have a txt file that contains name, id number, mobilenumber, and location in comma separated line.
example
Robby, 7890,7788992356, 123 westminister
tom, 8820, 77882345, 124 kingston road
My task is to retrieve
Look up all of an employee's information by name.
Look up all of an employee's information by ID.
Add the information of an employee.
Update the information of an employee.
SO far I have read the file and stored the information in a vector. Code is shown below.
For tasks
1)Look up all of an employee's information by name. I will iterate in the vector and prints information containing the name . I will be able to do that
2) simialry in text file I will look for id and prints information about that.
BUT I am clueless about point 3 & 4.
I am posting my code below
void filter_text( vector<string> *words, string name)
{
vector<string>::iterator startIt = words->begin();
vector<string>::iterator endIt = words->end();
if( !name.size() )
std::cout << " no word to found for empty string ";
while( startIt != endIt)
{
string::size_type pos = 0;
while( (pos = (*startIt).find_first_of(name, pos) ) != string::npos)
std:cout <<" the name is " << *startIt<< end;
startIt++;
}
}
int main()
{
// to read a text file
std::string file_name;
std::cout << " please enter the file name to parse" ;
std::cin >> file_name;
//open text file for input
ifstream infile(file_name.c_str(), ios::in) ;
if(! infile)
{
std::cerr <<" failed to open file\n";
exit(-1);
}
vector<string> *lines_of_text = new vector<string>;
string textline;
while(getline(infile, textline, '\n'))
{
std::cout <<" line text:" << textline <<std::endl;
lines_of_text->push_back(textline);
}
filter_text( lines_of_text, "tony");
return 0;
}
#include <string>
#include <iostream>
#include <vector>
#include <stdexcept>
#include <fstream>
struct bird {
std::string name;
int weight;
int height;
};
bird& find_bird_by_name(std::vector<bird>& birds, const std::string& name) {
for(unsigned int i=0; i<birds.size(); ++i) {
if (birds[i].name == name)
return birds[i];
}
throw std::runtime_error("BIRD NOT FOUND");
}
bird& find_bird_by_weight(std::vector<bird>& birds, int weight) {
for(unsigned int i=0; i<birds.size(); ++i) {
if (birds[i].weight< weight)
return birds[i];
}
throw std::runtime_error("BIRD NOT FOUND");
}
int main() {
std::ifstream infile("birds.txt");
char comma;
bird newbird;
std::vector<bird> birds;
//load in all the birds
while (infile >> newbird.name >> comma >> newbird.weight >> comma >> newbird.height)
birds.push_back(newbird);
//find bird by name
bird& namebird = find_bird_by_name(birds, "Crow");
std::cout << "found " << namebird.name << '\n';
//find bird by weight
bird& weightbird = find_bird_by_weight(birds, 10);
std::cout << "found " << weightbird.name << '\n';
//add a bird
std::cout << "Bird name: ";
std::cin >> newbird.name;
std::cout << "Bird weight: ";
std::cin >> newbird.weight;
std::cout << "Bird height: ";
std::cin >> newbird.height;
birds.push_back(newbird);
//update a bird
bird& editbird = find_bird_by_name(birds, "Raven");
editbird.weight = 1000000;
return 0;
}
Obviously not employees, because that would make your homework too easy.
So, first off, I don't think you should store the information in a vector of strings. This kind of task totally calls for the use of a
struct employee {
int id;
std::string name;
std::string address;
//... more info
};
And storing instances of employees in an
std::vector<employee>
You see, using your strategy of storing the lines, searching for "westminster" would net me Robbie, as his line of text does include this substring, but his name isn't westminster at all. Storing the data in a vector of employee structs would eliminate this problem, and it'd make the whole thing a lot more, well, structured.
Of course you'd need to actually parse the file to get the info into the vector. I'd suggest using a strategy like:
while(getline(infile, textline, '\n')) {
std::stringstream l(textline);
getline(l,oneEmp.name, ','); //extract his name using getline
l >> oneEmp.id; //extract his id
//extract other fields from the stringstream as neccessary
employees.push_back(oneEmp);
}
As for adding information: when the user enters the data, just store it in your employees vector; and when you should need to update the file, you may simply overwrite the original data file with a new one by opening it for writing & dumping the data there (this is obviously a rather wasteful strategy, but it's fine for a school assignment (I suppose it's school assignment)).
Start by splitting the CSV line into separate fields and then populate a struct with this data
eg:
struct Employee
{
std::string name;
std::string id_number;
std::string mobilenumber;
std::string location;
};
std::vector<Employee> employees; // Note you dont need a pointer
Look at string methods find_first_of, substr and friends.