how to manipulate the txt file using C++ STL HOmework - c++

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.

Related

Printing a text file which is stored line by line

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

Splitting input string c++ [duplicate]

This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 6 years ago.
I am reading in a file, that contains data in this format on each line. 30304 Homer Simpson I need to be able to pass this to the following constructor, the integer being the regNo, the name the rest of the string, and every student would have their own map of marks.
Student::Student (string const& name, int regNo):Person(name)
{
regNo = regNo;
map<string, float> marks;
}
I then have to add each student to a collection of students, which would be best, and how do I do this?
So far all I've got is getting the file name and checking it exists.
int main()
{
//Get file names
string studentsFile, resultsFile, line;
cout << "Enter the Students file: ";
getline(cin, studentsFile);
cout << "Enter the results file: ";
getline(cin, resultsFile);
//Check for students file
ifstream students_stream(studentsFile);
if (!students_stream) {
cout << "Unable to open " << studentsFile << "\n";
return 1;
}
}
I tried using getline with 3 arguments and " " as the delimiter but that would also split the name part of the string, so I'm not sure how to do this another way.
Replace std::cin with your input file stream of course. It would be probably sane to "trim" the name result, unless you know by 100% the input is well formatted. I added only bare-minimal error state handling to somehow "survive".
Names are read also for single/three/more variants of course, as any real world application should.
#include <iostream>
#include <string>
#include <stdexcept>
int main()
{
std::string line, name;
unsigned long long regNo;
size_t nameOfs;
while (true) {
// Read full non-empty line from input stream
try {
std::getline(std::cin, line);
if (line.empty()) break;
}
catch(const std::ios_base::failure & readLineException) {
break;
}
// parse values:
// 1. unsigned long long ending with single white space as "regNo"
// 2. remaining part of string is "name"
try {
regNo = std::stoull(line, &nameOfs);
name = line.substr(nameOfs + 1);
}
catch(const std::logic_error & regNoException) {
// in case of invalid input format, just stop processing
std::cout << "Invalid regNo or name in line: [" << line << "]";
break;
}
// here values regNo + name are parsed -> insert them into some vector/etc.
std::cout << "RegNo [" << regNo << "] name [" << name << "]\n";
}
}
A regular expression could be used:
We can then select group 2 and 3 from the result.
std::vector<Student> students;
std::regex r{R"(((\d+) )(.+))"};
for(std::string line; getline(students_stream, line);) {
auto it = std::sregex_iterator(line.begin(), line.end(), r);
auto end = std::sregex_iterator();
if(it == end || it->size() != 4)
throw std::runtime_error("Could not parse line containing the following text: " + line);
for(; it != end; ++it) {
auto match = *it;
auto regNo_text = match[2].str();
auto regNo{std::stoi(regNo_text)};
auto name = match[3].str();
students.emplace_back(name, regNo);
}
}
Live demo
You can take input using getline()and read one complete line(no third argument) and then use stringstream to extract the number and the remaining string. Example of stringstream:
string s = "30304 Homer Simpson", name;
stringstream ss(s);
int num;
ss >> num; //num = 30304
getline(ss, name); //name = Homer Simpson
cout << num;
cout << name;

Easy Program: Store Data from a file into a Struct Array

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;
}

how to extract out data from a txt file which is seperated by |

I am looking for a way to extract out data from a txt file which data is seperated by row and each column of data is seperaed by |
Here's an example
12|john bravo|123 kings street
15|marry jane|321 kings street
Previously i did it by separating using spaces like this
12 john kingstreet
15 marry kingstreet
But it poses a problem when I add a last name to the names/ add an address with spaces, ex: john bravo
So I decided to separate the column data using |
this is how I extract the data
struct PERSON{
int id;
string name;
string address;
};
//extract
int main(){
PERSON data[2];
ifstream uFile("people.txt");
int i = 0;
while(uFile >> data[i].id >> data[i].name >> data[i].address){
i++;
}
return 0;
}
So how do i extract if the columns are separated by | ??
Use getline() twice:
First, get each line use default seperator (new line); second, for each segment from first step, use '|' as seperator. "stringstream" class may be used to transfer data.
#edward The code below is modified from yours, and I think #P0W58 's answer is better.
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
const int length = 2;
struct PERSON
{
int id;
string name;
string address;
};
//extract
int main()
{
PERSON data[length];
ifstream fin("people.txt");
int i = 0;
while(true)
{
string segment;
if (!getline(fin, segment))
break;
stringstream transporter;
transporter << segment;
string idString;
getline(transporter, idString, '|');
getline(transporter, data[i].name, '|');
getline(transporter, data[i].address, '|');
stringstream idStream;
idStream << idString;
idStream >> data[i].id;
i++;
}
for (i=0; i<length; i++)
cout << data[i].id << '+' << data[i].name << '+'\
<< data[i].address << endl;
return 0;
}
To read into a struct , I'd overload << and then parse the text as mentioned in one of the answer .
Something like this :
#include<sstream>
//...
struct PERSON{
int id;
std::string name;
std::string address;
friend std::istream& operator >>(std::istream& is, PERSON& p)
{
std::string s;
std::getline(is, s); //Read Line, use '\r' if your file is saved on linux
std::stringstream ss(s);
std::getline(ss, s, '|'); //id
p.id = std::atoi(s.c_str());
std::getline(ss, p.name, '|'); // name
std::getline(ss, p.address, '|'); //address
return is ;
}
};
And then you can probably do,
std::ifstream fin("input.txt");
PERSON p1;
while (fin >> p1)
//std::cout << p1.id << p1.name << std::endl ;
You can overload << too
Use boost::tokenizer or find first of like :
// code example
string s = "12|john bravo|123 kings street";
string delimiters = "|";
size_t current;
size_t next = -1;
do
{
current = next + 1;
next = s.find_first_of( delimiters, current );
cout << s.substr( current, next - current ) << endl;
}
while (next != string::npos);

Reading information into array of structs c++

I'm making a program for my c++ class. Ultimately I want my program to perform a quicksort on a text file of contacts in the following format:
Firstname Secondname Number
Each contact is separated by a new line. I've started by counting the number of lines and using dynamic memory allocation to create an array of structs which has the same size as the number of lines.
However, when I tried to read in the information from the text file and output it to the screen, all I get is gibberish. I've had a look around on the internet to try and find a solution but everything I've found seems to use a different syntax to me.
Here's my code so far:
#include <iostream>
#include <fstream>
#include <istream>
char in[20];
char out[20];
using namespace std;
struct contact
{
char firstName[14];
char surName[14];
char number[9];
};
//structure definition
int main(void){
cout << "Please enter the input filename: " << endl;
cin >> in;
ifstream input(in);
if(!input){
cerr << "failed to open input file " << in << endl;
exit(1);
}
cout << "Please enter tne output filename: " << endl;
cin >> out;
// read in the input and output filenames
char a;
int b=0;
while (input.good ())
{
a=input.get ();
if (a=='\n')
{
b++;
}
}
// count the number of lines in the input file
input.seekg (0, ios::beg);
//rewind to beginning of file
contact* list = new contact[b];
//dynamically create memory space for array of contacts
int i = 0.;
while(input){
if(i >= b) break;
if(input >> *list[i].firstName >> *list[i].surName >> *list[i].number) i++;
else break;
}
input.close();
//read information from input file into array of contacts
for(int N = 0; N < b; N++){
cout << list[N].firstName << list[N].surName << list[N].number << endl;
}
ofstream output(out);
int k = 0;
for(int k = 0; k<b; k++){
output << list[k].firstName << " " << list[k].surName << " " << list[k].number << endl;
}
//print out the unsorted list to screen and write to output file
//i've done both here just to check, won't print to screen in final version
output.close();
delete []list;
} // end of main()
You reset the files location to the beginning, but the files eofbit is still labeled as true from when you first read the amount of lines. A quick fix to this is re-opening the file after you read the lines, possibly making the line count a function to clean up code.
int lines(const string path)
{
ifstream tmp(path.c_str());
string temp;
int count = 0;
getline(inFile,temp);
while(inFile)
{
count++;
getline(inFile,temp);
}
tmp.close();
return count;
}
Okay, I put together a quick and dirty method using newer C++ constructs to get you most of the way there. You're on your own for writing to the file (trivial) and the quicksort, though I've put the struct into a vector for you, so sorting the vector is as easy as writing a custom function to compare one struct vs the other. I apologize in advance if some of the code is less than canonical C++. I'm way past my bed time, and way tired, but this was interesting enough of a problem that I wanted to give it a go. Happy coding!
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
#include <sstream>
using namespace std;
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
struct contact
{
std::string firstName;
std::string surName;
std::string number;
contact(std::string& fName, std::string& lName, std::string& num) : firstName(fName), surName(lName), number(num) {}
};
//structure definition
char in[20];
char out[20];
int main()
{
std::vector<contact> contacts;
cout << "Please enter the input filename: " << endl;
cin >> in;
ifstream input(in);
if(!input){
cerr << "failed to open input file " << in << endl;
exit(1);
}
cout << "Please enter tne output filename: " << endl;
cin >> out;
std::string sinput;
// read in the input and output filenames
while (input.good ())
{
getline(input, sinput);
vector<string> tokens = split(sinput, ' ');
if (tokens.size() == 3)
{
contact c(tokens[0], tokens[1], tokens[2]);
contacts.push_back(c);
}
}
input.close();
//read information from input file into array of contacts
std::cout << "Outputting from vector..." << std::endl;
for_each(contacts.begin(), contacts.end(), [](contact& c) {
cout << c.firstName << " " << c.surName << " " << c.number << endl;
});
return 0;
}
Also, just want to give credit that the split methods come from this answer on this very site. Cheers!