How to search line by line in a text file - c++

123 Michael
456 Calimlim
898 Mykfyy
999 Kyxy
657 mykfyy
898 Help
I'm creating a student attendance keeper system. One of the features of my system is that the student need to register(his/her id and name) first to access the system (login with his/her id)
The problem is that i don't know and i don't want my student to have a similar ID number Like(e.g 898 Mykfyy and 898 Help)
I'm using fstream in my system. I've been thinking that if I want to avoid the duplication i need to Read(ifstream) the .txt file before register(oustream). But i dont know how to read line by line and check if the ID(898) is already use/existed

In C++ one wouldn't deal with lines, but with objects:
#include <limits>
#include <cstdlib>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
#include <algorithm>
struct student_t
{
unsigned id;
std::string name;
};
bool operator==(student_t const &lhs, student_t const &rhs)
{
return lhs.id == rhs.id;
}
std::ostream& operator<<(std::ostream &os, student_t const &student)
{
return os << student.id << ' ' << student.name;
}
std::istream& operator>>(std::istream &is, student_t &student)
{
unsigned id;
if (!(is >> id))
return is;
std::string name;
if (!std::getline(is, name)) {
return is;
}
student = student_t{ id, name };
return is;
}
int main()
{
char const *filename{ "test.txt" };
std::ifstream input{ filename };
if (!input.is_open()) {
std::cerr << "Couldn't open \"" << filename << "\" for reading :(\n\n";
return EXIT_FAILURE;
}
std::vector<student_t> students{ std::istream_iterator<student_t>{ input }, std::istream_iterator<student_t>{} };
input.close();
std::copy(students.begin(), students.end(), std::ostream_iterator<student_t>{ std::cout, "\n" });
student_t new_student;
while (std::cout << "New Student?\n", !(std::cin >> new_student)) {
std::cerr << "Input error :(\n\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
auto it{ std::find(students.begin(), students.end(), new_student) };
if (it != students.end()) {
std::cerr << "Sorry, but a student with id " << new_student.id << " already exists :(\n\n";
return EXIT_FAILURE;
}
std::ofstream output{ filename, std::ios::app };
if (!output.is_open()) {
std::cerr << "Couldn't open \"" << filename << "\" for writing :(\n\n";
return EXIT_FAILURE;
}
output << new_student << '\n';
std::cout << "New student [" << new_student << "] added :)\n\n";
}

The easiest way to do it is probably using std::getline to get the current line as a string:
using namespace std;
ifstream in(fileName);
string line;
while(getline(in, line))
{
// --do something with the line--
}
You will then need to parse each line to get the correct ID
Edit: updated to remove eof()

Depends on how you implemented it.
I suppose the number of people isn't too large so I would just check the Id's before a new ID is added.
Something like
If ID exists, not add.

Related

fread is reading garbage in file

I am working on my assignment and faced a problem with fread() in C++. When I modify the name in my file it modifies it perfectly as I want but the problem occurs when I try to read the file after that, it reads the whole file but it does not stop after that it's running total 146 times whereas there are only 3 names.
My code:-
#include <bits/stdc++.h>
using namespace std;
struct person{
int id;
string fname;
}s;
void write(){
FILE *outfile;
struct person input;
int num,ident;
string sname[] = {"a","b","c"};
outfile = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","wb");
if (outfile == NULL)
{
fprintf(stderr, "\nError opend file\n");
exit (1);
}
scanf("%d",&num);
for(int i=0;i<num;i++){
s.fname = sname[i];
cin >> s.id;
fwrite (&s, sizeof(s), 1, outfile);
}
fclose(outfile);
}
void read(){
FILE *file1;
int i=0;
file1 = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","r");
while(fread(&s, sizeof(s), 1, file1) == 1) {
cout << "ID " << s.id << " Name " <<s.fname << endl;
}
fclose (file1);
}
void modify(){
FILE *file;
file = fopen ("C:\\Users\\Amritesh\\Desktop\\students.txt","r+");
while(fread(&s, sizeof(s), 1, file)) {
if(s.fname == "a"){
s.fname = "d";
fseek(file,-sizeof(s),SEEK_CUR);
fwrite (&s, sizeof(s), 1,file);
}
}
fclose (file);
}
int main(){
write();
modify();
read();
}
Edited code:-
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct person
{
int id;
string fname;
}s,temp;
void read()
{
int num;
ifstream fin;
fin.open("C:\\Users\\Amritesh\\Desktop\\student.txt",ios::in);
fin.seekg(0,ios::beg);
//scanf("%d",&num);
while(fin){
cout << s.fname << s.id << endl;
}
fin.close();
}
void write(){
int i=0;
ofstream fout;
fout.open("C:\\Users\\Amritesh\\Desktop\\student.txt");
while(i!=2) {
cin >> s.id >> s.fname;
fout << "ID " << s.id << " Name " <<s.fname << endl;
i++;
}
fout.close();
}
void modify(){
fstream mod;
mod.open ("C:\\Users\\Amritesh\\Desktop\\student.txt");
while(mod) {
if(s.fname == "a"){
s.fname = "d";
mod.seekg(-sizeof(s),ios::cur);
mod << s.fname;
}
}
mod.close();
}
int main(){
write();
read();
modify();
}
Thanks for any answer!
Here are three ideas based on our discussion. I'll start with free functions for reading and writing a person object since it looks like you're at that stage right now. I'll move on to adding member functions in your person class and end with adding stream operators for convenience.
An example of free (non-member) read and write functions:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
};
std::ostream& write(std::ostream& os, const person& p) {
os << p.id << ',' << p.fname << '\n'; // stream out the properties of a person
return os; // look at the next example for an alternative doing the same thing
}
std::istream& read(std::istream& is, person& p) {
// extract "id" and if it succeeds, check if the next char is a , char
if(is >> p.id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, p.fname); // read the rest of the line into p.fname
} else {
// we didn't get id or the , char, so set the stream in a failed state
is.setstate(is.failbit);
}
return is;
}
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
write(file, test1);
write(file, test2);
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(read(file, test)) {
std::cout << test.fname << '\n';
}
}
}
An example of making read and write member functions in person:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
std::ostream& write(std::ostream& os) const {
// this does the same thing as in the first example
return os << id << ',' << fname << '\n';
}
std::istream& read(std::istream& is) {
if(is >> id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, fname);
} else {
is.setstate(is.failbit); // we didn't get id or the , char
}
return is;
}
};
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
test1.write(file);
test2.write(file);
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(test.read(file)) {
std::cout << test.fname << '\n';
}
}
}
Member functions supported by stream operators:
#include <iostream>
#include <string>
#include <fstream>
struct person {
int id;
std::string fname;
std::ostream& write(std::ostream& os) const {
return os << id << ',' << fname << '\n';
}
std::istream& read(std::istream& is) {
if(is >> id && is.peek() == ',') {
is.ignore(); // step over the , char
std::getline(is, fname);
} else {
is.setstate(is.failbit); // we didn't get id or the , char
}
return is;
}
};
// stream operators calling member functions
std::ostream& operator<<(std::ostream& os, const person& p) { return p.write(os); }
std::istream& operator>>(std::istream& is, person& p) { return p.read(is); }
int main() {
// write to file
{
std::ofstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test1{10, "Foo Bar"};
person test2{20, "Apa Bepa"};
file << test1 << test2; // calling operator<< for each person object
}
// read from file
{
std::ifstream file("C:\\Users\\Amritesh\\Desktop\\student.txt");
person test;
while(file >> test) { // extract one person at a time using operator>>
std::cout << test.fname << '\n';
}
}
}

Overloaded constructor implementing private class members

In this code I have an overloaded constructor Record::Record(string s)that reads in a string, I am trying to create a string stream from 's' and use getline(stringStream, line, ",") to read each element from the string with "," as the delimiter, then assign each element to the appropriate object variable. The end goal of this assignment is to open a file, read in its data, assign the data appropriately in a vector, then write and parse the data to a new file.
My understanding of working with private class members is limited. I am unsure how to go about writing the constructor. In the past I've used a pointer for private members (e.g 'this-> foo;), at this point I just need to understand how to implement the contents of Record, so far what I've tried has been incorrect and I can only find references to using pointers to int's.
Normally I would go to my comp-sci lab and ask a TA but it is currently close due to COVID.
Here is the code for my constuctors and overloaded operators.
Record.cpp
#include <string>
#include <sstream>
#include "Record.h"
using namespace std;
Record::Record(string s) {
/** here is where I need to assign data to the following.
std::string department;
std::string item_code;
int quantity;
double cost; **/
}
Record::~Record() {
// TODO Auto-generated destructor stub
}
//overloaded "==" and "<" comparison operators
bool operator ==(const Record &lhs, const Record &rhs){
return (lhs.cost == rhs.cost && lhs.department == rhs.department &&
lhs.item_code == rhs.item_code && lhs.quantity == rhs.quantity);
}
/**bool operator <(const Record &a, const Record &b){ //do not need at this time
}
**/
//Overloaded "<<" operator
std::ostream& operator <<(std::ostream& os, const Record& r){
os << r.department << ',' << r.item_code << ',' << r.quantity << ',' << r.cost;
return os;
}
Record.h
#ifndef RECORD_H_
#define RECORD_H_
#include <iostream>
#include <string>
class Record {
public:
//Constructor
Record(std::string s); //pass this string to our Record class
//De-constructor
virtual ~Record();
//overloaded "==" and "<" comparison operators
friend bool operator ==(const Record &a, const Record &b);
//friend bool operator <(const Record &a, const Record &b); //Do not need at this time.
//Overloaded "<<" operator
friend std::ostream& operator <<(std::ostream&, const Record&);
private:
std::string department;
std::string item_code;
int quantity;
double cost;
};
#endif /* RECORD_H_ */
Main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <libgen.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "Record.h"
using namespace std;
int main() {
vector<Record> records; //vector of type Records to hold each "Line" of input file
string filename; // File name and path stored as string
/**
* Prompts user for the name of input file and stores in string variable filename
*
*/
cout << "please enter the name of your file with file path:" << endl;
cin >> filename;
ifstream ifs { filename.c_str() };
if (!ifs) {
cerr << " Can't open file " << filename << endl;
return 1;
}
string path = filename.substr(0, filename.find_last_of("\\/"));
string file = filename.substr(filename.find_last_of("\\/") + 1,
filename.length());
if (path.compare(file) == 0) {
path == "";
}
//test for file and file path
cout << "Path portion of " << filename << " is " << path << endl;
cout << "File portion of " << filename << " is " << file << endl; // path + "new_" + file + ".cvs", make new file with new path
/**
* Put each line of input file in to the records vector
*/
string line; //strings for each parameter of the vector object
while (getline(ifs, line)) {
Record newRecord(line); //this should check for duplicates and ignore any found.
int i = 0;
int n = 0;
if((newRecord == records[i]) || (i < n) ){
i++;
}
else{
records.push_back(newRecord);
n++;
i = 0;
}
}
ifs.close(); //closes the stream
//create new file and output data to it
string newFile = ("new_" + file + ".cvs");
//check to see if file path and file name are correct
cout << (path + newFile);
//Open stream to new file and write to it
ofstream ofs(path + newFile);
ofs.open(newFile);
for(size_t i = 0; i < records.size(); i++){
ofs << (i+1) << ' ' << records[i];
}
ofs.close(); //close output stream
return 0;
}
You can do something like:
Record::Record(std::string s){
std::string word;
std::vector<std::string> temp;
std::stringstream ss(s);
while(getline(ss, word, ','))
temp.push_back(word);
department = temp.at(0);
item_code = temp.at(1);
quantity = stoi(temp.at(2));
cost = stod(temp.at(3));
}
I'm assuming you mean that each parmeter is separated by ',', not each line, if that's not the case, say something.
Edit
So there are a couple of issues in your main function, namely, the while getline cycle will probably have out_of_range access, you can use a range-based for loop, which avoids container overflow:
while (getline(ifs, line))
{
bool flag = 1;
Record newRecord(line);
for(Record r : records){
if(r == newRecord){
flag = 0;
break;
}
}
if(flag)
records.push_back(newRecord);
}
The ofstream file is not being properly opened and tested, you can do something like:
ofstream ofs;
ofs.open(path + newFile);
if (!ofs)
{
cerr << "Can't open file " << filename << endl;
return 1;
}
for (size_t i = 0; i < records.size(); i++)
{
ofs << (i + 1) << ' ' << records[i] << endl;
}
This line:
path == "";
I'm sure you meant:
path = "";
Running sample
One last note, using namespace std is not a very good practice.

C++ read line to class vector

I have next c++ class called "Contact":
class Contact
{
private:
std::string contactName;
double subscriptionPrice;
int minutesIncluded;
public:
Contact(const std::string &contactName, double subscriptionPrice,
int minutesIncluded) : contactName(contactName), subscriptionPrice(subscriptionPrice), minutesIncluded(minutesIncluded)) {}
Contact() {
}
...gettetrs and setters
}
I have text file with one or more contacts in format:
asd,1.00000,1
In main method I have method that add properly vector of contacts in this text file. Problem is when I try to read from it. My target is to convert text file into vector of contacts. Method I use is next:
void phonebook_load(vector<Contact> &contacts)
{
string line;
ifstream phonebook_file;
vector<std::string> lines;
phonebook_file.open(phonebook_filename);
if(!phonebook_file.is_open())
cout << "Phonebook file could not be openned !!!" << endl;
else
{
while (phonebook_file.good())
{
for (string line; getline(phonebook_file, line, ','); )
lines.push_back(line);
}
phonebook_file.close();
}
}
I have two options:
Read line by line (which I cannot split by ",")
Split by "," which print every property of contact on new line, and I don't see how tho handle it from there.
What should I change in my method in order to read file line by line and properly convert it to vector<Contact>
Provide stream extraction and stream insertion operators for your type:
#include <string>
#include <vector>
#include <iterator>
#include <fstream>
#include <iostream>
class Contact
{
private:
std::string contactName;
double subscriptionPrice;
int minutesIncluded;
public:
Contact() {}
Contact(const std::string &contactName, double subscriptionPrice, int minutesIncluded)
: contactName { contactName },
subscriptionPrice { subscriptionPrice },
minutesIncluded { minutesIncluded }
{}
// declare the stream extraction and stream insertion operators as firends
// of your class to give them direct access to members without the need for
// getter and setter functions.
friend std::istream& operator>>(std::istream &is, Contact &contact);
friend std::ostream& operator<<(std::ostream &os, Contact const &contact);
};
std::istream& operator>>(std::istream &is, Contact &contact)
{
std::string contact_name;
if (!std::getline(is, contact_name, ',')) // use getline with a delimiter
return is; // to allow whitespace in names
// which >> doesn't
char seperator;
double subscription_price;
int minutes_included;
if (!(is >> subscription_price >> seperator >> minutes_included) || seperator != ',') {
is.setstate(std::ios::failbit);
return is;
}
contact = Contact{ contact_name, subscription_price, minutes_included };
return is;
}
std::ostream& operator<<(std::ostream &os, Contact const &contact)
{
os << contact.contactName << ", " << std::fixed << contact.subscriptionPrice
<< ", " << contact.minutesIncluded;
return os;
}
int main()
{
std::ifstream is{ "test.txt" };
std::vector<Contact> contacts{ std::istream_iterator<Contact>{ is },
std::istream_iterator<Contact>{} };
for (auto const &c : contacts)
std::cout << c << '\n';
}

How to replace a string with another string in csv file using C++

I am opening a csv file which contains entries in the form "key,value".
Task is to find a particular key and modify its corresponding value with another string. (key & value : both are strings).
int main()
{
std::fstream m_file;
m_file.open("input.csv", std::ios::in | std::ios::out);
m_file << "Star,Treks\n";
m_file << "Captain,America\n";
m_file << "Black,Cats\n";
m_file << "Ninja,Fighters\n";
std::string row;
std::string key = "Black";
std::string value = "Dreamers";
while (std::getline(m_file, row)) {
std::size_t pos = row.find(',');
std::string retrieved_key = row.substr(0, pos);
std::string retrieved_value = row.substr(pos + 1);
if (retrieved_key == key) {
std::size_t currentPos = m_file.tellg();
std::size_t row_length = row.length();
m_file.seekp(currentPos - row_length);
std::string newEntry = key + "," + value + "\n";
m_file << newEntry;
return 0;
}
}
getchar();
return 0;
}
This is the code I have written. But it does not work. It modifies the contents of the file as below :
Star,Treks
Captain,America
BlBlack,Dreamers
Fighters
You cannot modify files directly using seekg() and tellg() functions unless these have a fixed size format of the parts you want to change.
The usual way to do such things is to create an intermediary temp file to write your changes, and replace the original one with it after you're done.
Recipe:
#include <cstdlib>
#include <iostream>
#include <utility>
#include <string>
#include <fstream>
#include <vector>
#include <iterator>
#include <algorithm>
class pair : public std::pair<std::string, std::string>
{
public:
using std::pair<std::string, std::string>::pair;
bool operator==(pair const & rhs) const { return first == rhs.first; }
friend std::istream& operator>>(std::istream &is, pair &p)
{
std::getline(is, p.first, ',');
std::getline(is, p.second, '\n');
return is;
}
friend std::ostream& operator<<(std::ostream &os, pair const &p)
{
os << p.first << ',' << p.second << '\n';
return os;
}
};
int main()
{
char const *filename = "input.csv";
std::fstream file{ filename, std::ios::in };
if (!file) {
std::cerr << "Failed to open file \"" << filename << "\"!\n\n";
return EXIT_FAILURE;
}
std::vector<pair> data{
std::istream_iterator<pair>{ file },
std::istream_iterator<pair>{}
};
file.close();
auto it{ std::find(data.begin(), data.end(), pair{ "Black", "" }) };
if (it == data.end()) {
std::cerr << "Key not found!\n\n";
return EXIT_FAILURE;
}
it->second = "Dreamers";
file.open(filename, std::ios::out | std::ios::trunc);
if (!file) {
std::cerr << "Failed to open file \"" << filename << "\"!\n\n";
return EXIT_FAILURE;
}
for (auto const & entry : data)
file << entry;
}

Writing a C++ Program to Search an Index File From the Linux Command Line

I've written a program that reads in a data file and creates a sorted index file from the data in the original file. However, I"m then supposed to write a second program that allows teh user to search this index file from the Linux command line. For example, they are supposed to be able to type
search 12382 prog5.idx
into the command line and have the information for that record displayed. I have no idea how to accomplish this.
I have written the code to create the index file (works):
#include <iostream>
#include <fstream>
#include <map>
#include <sstream>
#include <string>
#include <iomanip>
using namespace std;
class Record {
string name;
int code;
double cost;
public:
Record() {
}
Record(string tname,int tcode,double tcost) : name(tname),code(tcode),cost(tcost) {
}
friend ostream& operator<< (ostream &os, const Record& r);
};
//print function
ostream& operator<< (ostream &os, const Record& r) {
os << setw(10) << r.name << " " << setw(5) << r.code << " $" << setw(10) << setprecision(2) << fixed << r.cost ;
return os;
}
int main() {
std::map<int, Record> myMap;
ifstream data;
size_t offset_count = 0;
data.open("prog5.dat");
ofstream outFile("prog5.idx", ios::out);
//if file can't be opened, exit
if(!data) {
cerr << "Open Failure" << endl;
exit(1);
}
std::string line;
while (std::getline(data, line)) {
stringstream ss(line);
int key;
string name;
int code;
double cost;
if(ss >> key >> name >> code >> cost) {
Record r(name,code,cost);
myMap.insert( pair<int,Record>(key,r));
}
else {
cout << "Error";
}
}
// print what's stored in map
for(std::map<int,Record>::iterator x = myMap.begin(); x!=myMap.end(); ++x) {
cout << setw(10) << x->first << ": " << x->second << endl;
}
}
And get the following output when running the above code:
8: blank 0 $ 0.00
12165: Item16 30 $ 7.69
12345: Item06 45 $ 14.20
12382: Item09 62 $ 41.37
12434: Item04 21 $ 17.30
16541: Item12 21 $ 9.99
21212: Itme31 19 $ 8.35
34186: Item25 18 $ 17.75
41742: Item14 55 $ 12.36
Here's what I have so far for the second program:
#include <prog3.idx>
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
if(argc < 3) {
std::cerr << "Too few arguments \n";
std::exit(EXIT_FAILURE);
}
int key = atoi(argv[1]);
const char* filename = argv[2];
ifstream input;
input.open("prog5.idx");
}
But I'm not sure where to go from there. Can someone help me out?
Use map or multimap and find in the STL. Make a datatype out of the remaining data with the index as the key. The program will have to read in the entire file first and then find the searched index.