How to read specific data from a file that looks like this? - c++

I want to make a program that inputs data of participants in a txt file through the input function of the class. The output function is then used to extract the information of a single participant at a time by keying in their ID.
In this code of mine, my while loop runs infinitely as soon as I enter an ID. I suspect that it is unable to find the eof(). Any help would be greatly appreciated. I am new to C++.
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
class Participant{
private:
int id, score;
string name;
public:
Participant(){
id = 0; score = 0; name = "";
}
void input(){
char choice;
ofstream in;
in.open("Participant.txt", ios::app);
do{
cout<<"Enter your ID: \t";
cin>>id;
cout<<"Enter your name: \t";
cin>>name;
cout<<"Enter your Score:\t";
cin>>score;
in<<name<<" ";
in<<id<<" ";
in<<score<<endl;
cout<<"Add another entry? (Y/N)\n";
cin>>choice;
}while(choice == 'y' || choice == 'Y');
in.close();
}
void output(){
int idout, holderID, holderS;
string holder, output;
cout<<"Enter the ID for more information related to the person:";
cin>>idout;
fstream out;
out.open("Participant.txt");
while(!out.eof()){
out>>holderID;
cout<<"looping...\n";
if(idout == holderID){
out>>holder;
cout<<"Name: \t"<<holder<<endl;
out>>holderS;
cout<<"Score:\t"<<holderS<<endl;
holder ="";
holderS=0;
break;
}
else continue;
}
out.close();
}
void max(){
}
};
int main(){
char choice;
Participant player;
cout<<"Asking for Input: \n";
player.input();
system("pause");
system("cls");
cout<<"Data Viewing: \n";
do{
player.output();
cout<<"\nDo you wish to extract information on other players?\n";
cout<<"Y - Yes."<<endl;
cout<<"N - No."<<endl;
cout<<"Choice: ";
cin>>choice;
}while (choice == 'y' || choice == 'Y');
cout<<"\n\nEnd of Data Viewing.\n";
}
I want it to, at first, read just the ID, in the first line its 1037. If the ID matches, it should display the next 2 members in the file; the name and the score.

The Problem is that you tried to use holderID (int) Directly from the out stream. Try to use string to read the same out value and use stoi() to convert the same to int.
Also note as you write the first is name followed by id and score.
Also use the below as a reference. I have used std::map to store the value of id, name and score.
#include <string>
#include <fstream>
#include <map>
#include <iostream>
#include <algorithm>
#include <sstream>
class Participants
{
int id;
int score;
std::string name;
public:
Participants(): id(0), score(0)
{}
Participants(int id, int score, std::string name): id(id), score(score), name(name)
{}
~Participants()
{}
int GetId()
{
return id;
}
std::string encode()
{
auto strRet = std::string( name + " " + std::to_string(id) + " " + std::to_string(score) + "\n");
return strRet;
}
void decode(std::string text)
{
std::stringstream ss(text);
std::string buf;
//Read Name
std::getline( ss, buf , ' ');
name = buf;
//Read id
std::getline( ss, buf , ' ');
id = std::stoi( buf );
//Read Score
std::getline( ss, buf , '\n');
score = std::stoi( buf );
}
};
class DataReader
{
std::string fileName;
std::fstream myfile;
public:
DataReader(std::string fileName): fileName(fileName)
{
}
~DataReader()
{
}
void ReadParticipants(std::map<int, Participants> &MapParticipants)
{
myfile.open(fileName, std::ios::in);
MapParticipants.clear();
if ( myfile.is_open() )
{
std::string line;
while ( std::getline(myfile, line) )
{
Participants oParticipants;
//Decode and Add to map
oParticipants.decode(line);
//Add to map
MapParticipants[ oParticipants.GetId() ] = oParticipants;
}
}
myfile.close();
}
void WriteParticipants(std::map<int, Participants> &MapParticipants)
{
//Load Map to find Duplicates
std::map<int, Participants> MapParticipants_exist;
ReadParticipants(MapParticipants_exist);
myfile.open(fileName, std::ios::app);
if ( myfile.is_open() )
{
for ( auto oParticipants : MapParticipants)
{
//Check for Duplicates (to Write or not)
if ( MapParticipants_exist.find(oParticipants.first) == MapParticipants_exist.end() )
{
auto text = oParticipants.second.encode();
myfile << text.c_str();
}
}
}
myfile.close();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
DataReader oReader("File.txt");
std::map<int, Participants> MapParticipants;
//Make Some Participants
Participants p1(1, 50, "TOM");
Participants p2(2, 40, "TIM");
Participants p3(3, 80, "JERRY");
//Add them to map
MapParticipants[p1.GetId()] = p1;
MapParticipants[p2.GetId()] = p2;
MapParticipants[p3.GetId()] = p3;
oReader.WriteParticipants(MapParticipants);
oReader.ReadParticipants(MapParticipants);
//Find and Display
int id = 2;
auto it = MapParticipants.find(id);
if ( it != MapParticipants.end() )
{
//Show/Print
...
}
return 0;
}

Related

How to skip reading the first line of file?

How can I ignore the first line of the text file and start at the second line when I called it in the code? I was wondering how. Also, how can I sort the file according to first name, last name and grade? I just have the first name sorted but not the last name and grade accordingly. If you have any idea, I hope you can share it with me. Thanks for the help! Here's my code:
#include <iostream>
#include <fstream>
using namespace std;
struct studentRecord{
string lastname;
string firstname;
string grade;
};
int main(){
ifstream ifs("student-file.txt");
string lastname, firstname, grade, key;
studentRecord records[20];
if(ifs.fail()) {
cout << "Error opening student records file" <<endl;
exit(1);
}
int i = 0;
while(! ifs.eof()){
ifs >> lastname >> firstname >> grade;
records[i].lastname = lastname;
records[i].firstname = firstname;
records[i].grade = grade;
i++;
}
for (int a = 1, b = 0; a < 20; a++) {
key = records[a].firstname ;
b = a-1;
while (b >= 0 && records[b].firstname > key) {
records[b+1].firstname = records[b].firstname;
b--;
}
records[b+1].firstname = key;
}
for (int k = 0; k < 20; k++) {
cout << "\n\t" << records[k].firstname << "\t"<< records[k].lastname << "\t" << records[k].grade;
}
}
When I saw this post it reminded me of a similar task completed at uni. I have rewritten your code to perform the same task but using classes instead of structs. I have also included a way to sort the vector by using the function here.
I have included the "ignore first line" method #Scheff's Cat mentioned.
Here it is:
#include <iostream>
#include <fstream>
#include <sstream>
#include <limits>
#include <string>
#include <vector>
using namespace std;
class studentrecord{
string firstname, lastname, grade;
public:
studentrecord(string firstname, string lastname, string grade){
this -> firstname = firstname;
this -> lastname = lastname;
this -> grade = grade;
}
friend ostream& operator<<(ostream& os, const studentrecord& studentrecord) {
os << "\n\t" << studentrecord.firstname << "\t" << studentrecord.lastname << "\t" << studentrecord.grade;
return os;
}
};
void displayRecords(vector <studentrecord*> records){
for(int i = 0; i < records.size(); i++){
cout << *records[i];
}
}
int main(){
//read in file
ifstream infile;
infile.open("student-file.txt");
infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
if (!infile.is_open()){
cout << "Error opening student records file" <<endl;
exit(1);
}
vector <studentrecord*> records;
string firstname, lastname, grade;
while (infile >> firstname >> lastname >> grade;) {
records.push_back(new studentrecord(firstname, lastname, grade));
}
displayRecords(records);
return 0;
}
To sort the vector so that it prints in order of either first name, last name or grade I used the following functions:
bool sortfirstname(studentrecord* A, studentrecord* B) {
return (A->getfirstname() < B->getfirstname());
}
bool sortlastname(studentrecord* A, studentrecord* B) {
return (A->getlastname() < B->getlastname());
}
bool sortgrade(studentrecord* A, studentrecord* B) {
return (A->getgrade() < B->getgrade());
}
sort(records.begin(), records.end(), (sortfirstname));
sort(records.begin(), records.end(), sortlastname);
sort(records.begin(), records.end(), sortgrade);
If you wanted to sort by first name you would call the sort(records.begin(), records.end(), (sortfirstname)); function and then the displayrecords() function.
The advantage of using classes stored in vectors is that you don't have to state the size of the vector containing the details about students since you can keep adding information to the end of the vector using the vector.push_back() function. It also makes sorting the data contained easier.
If anything isn't clear, let me know and I can give you a hand.

How to read from file and store in array of objects in c++

I am learning c++ and have a trouble in file handling. I am writing a code as a homework where i have to write objects into a file and then read those objects as array from the file at once. Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
class Records{
char* name;
int roll;
public:
Records()
{
name = new char[20];
}
void setData()
{
cout<<"Enter name: "<<endl;
cin>>name;
cout<<"Enter roll"<<endl;
cin>>roll;
}
char* getname()
{
return name;
}
int getRoll()
{
return roll;
}
void operator = (Records& no)
{
name = no.name;
roll = no.roll;
}
};
int main()
{
int i =0 ;
Records rec;
rec.setData();
Records::increase();
ofstream fout;
fout.open("file.txt", ios::app);
fout.write((char*)&rec, sizeof(rec));
fout.close();
Records* results = new Records[20];
Records rec1;
ifstream fin;
fin.open("file.txt", ios::in);
while(!fin.eof())
{
fin.read((char*)&rec1, sizeof(rec1));
results[i] = rec1;
i++;
}
fin.close();
cout<<results[0].getRoll();
return 0;
}
So basically, I made a Records class and store its object in a file. That works fine but I faced problem while taking data from file. It is not showing anything or sometimes showing garbage value. Anyone have better idea please hep me.
Thanks in advance!
First, you have to open file in binary mode for read and write.
std::ofstream fou("out_filename",std::ofstream::binary);
std::ifstream fin("in_filename", std::ifstream::binary);
Secondly, you assign operator=() is problematical. It assigns two records using the same address. Therefore in the reading process, all 20 elements in result[i] were all sharing the address of rec1::name. You have to correct the operator=() by copying contents of name.
This is not good.
void operator = (Records& no)
{
name = no.name;
roll = no.roll;
}
Rewrite as follows:
Edit: since your objects are all initially assigned with its memory. The new allocation is not necessary.
Records& Records::operator=(const Records& no)
{
// this->name = new char [20];
std::copy_n(no.name, 20, this->name); // include <algorithm>
roll = no.roll;
return *this; // return current object for another =.
}
Finally, add a destructor
Records::~Records() {
delete [] this->name; }
Good luck!
After fixed some other errors, I post this final version for you reference. Note that this project cannot use dynamic allocation for the field "name". Using dynamic allocation, the 20-byte of "name" is not counted as the size of class Records, and the pointer itself is not transferable. It causes read/write error in the field "name".
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
class Records{
char name[20];
int roll;
public:
Records()
{
// name = new char[20];
}
void setData()
{
cout<<"Enter name: "<<endl;
cin>>name;
cout<<"Enter roll"<<endl;
cin>>roll;
}
const char* getname() const
{
return name;
}
int getRoll() const
{
return roll;
}
Records& operator = (const Records& no)
{
std::copy_n(no.name, 20, this->name);
roll = no.roll;
return *this;
}
};
int main()
{
int i =0, c ;
std::string a;
Records rec;
ofstream fout;
fout.open("file.txt", std::ofstream::binary);
c = 0;
while (1)
{
std::cout << "Input record [" << c << "] ? (y/n) ";
std::cin >> a;
if (a[0]=='y' || a[0]=='Y')
{
rec.setData();
fout.write((char*)&rec, sizeof(rec));
++c;
}
else break;
}
fout.close();
// output
Records* results = new Records[20];
Records rec1;
ifstream fin;
fin.open("file.txt", std::ifstream::binary);
while(!fin.eof())
{
fin.read((char*)&rec1, sizeof(rec1));
results[i] = rec1;
i++;
}
fin.close();
// eidt to print all records
for (int j=0; j<(i-1); j++)
{ std::cout << "record # = " << j << std::endl;
std::cout << " name = " << results[j].name;
std::cout << " roll = " << results[j].roll << std::endl;
}
return 0;
}
A test run
$ ./a.exe
Input record [0] ? (y/n) y
Enter name:
aaaa
Enter roll
1234
Input record [1] ? (y/n) y
Enter name:
bbbb
Enter roll
2345
Input record [2] ? (y/n) y
Enter name:
cccc
Enter roll
3456
Input record [3] ? (y/n) n
1234

Read columns from a comma delimited data file c++

I have been trying to read following data table and create an object for the HUBs(rows) and another object for continent (columns). Since I am not a C++ experienced user I have been facing some difficulties. The data is in following. The number after HUB and the dash shows the order from the hub. The other numbers under each continent are the corresponding cost and tariffs between a HUB and continent. I would like to be able to cout for instance following and get the result which would be 73.
cout << hub(1)->cont(USA)->transport() << endl;
,USA,EUROPE,ASIA
HUB1-12000,,,
Transportation Cost,73,129,141
Tariffs,5,5,1
ShippingType,a,b,c
OtherFees,0.6,0.3,0.8
HUB2-11000,,,
Transportation Cost,57,101,57
Tariffs,7,7,5
ShippingType,b,b,d
OtherFees,0.7,0.3,0.6
Really appreciate your help. Here is what I have tried so far:
void Hub()
{
string file = "/hubs.csv";
// 1-First read the first line and save the continent name
string str, field;
getline( fin, str );
vector<string> contList;
stringstream linestr( str );
while ( linestr.good() )
{
getline( linestr, field, ',' );
string contname;
contList.push_back(contname);
}
// 2-Then read the rest
getline( fin, str );
while ( !fin.eof() ) // Read the whole file
{
stringstream linestr( str );
string contname, order;
if ( qstr[0] == 'HUB1' || qstr[0] == 'HUB2')
{
// Read the name of the hub
getline( linestr, hubname, ',' ); // Read the hub name
getline( linestr, order, ',' ); // Read the order quantityity
int quantity;
istringstream orderstream( order);
orderstream >> quantity;
// Find the hub and add the order to the hub
Hub* hub = glob->FindHubName( hubname ); // this returns a pointer
if ( glob->FindHubName( hubname ) == nullptr )
{
hubNotFound.push_back( hubname );
getline( fin, qstr );
continue;
}
hub->TotalOrder( quantity );
}
else if ( qstr[0] != 'HUB1' || qstr[0] != 'HUB2')
{
// Read costs and tariffs
cout << hub(1)->cont(ASIA)->transport()
}
getline( fin, qstr );
}
fin.close();
}
Something like this:
#include <iostream>
#include <fstream>
#include <boost/tokenizer.hpp>
#include <string>
int main() {
using namespace std;
using namespace boost;
string line, file_contents;
fstream file("test.csv");
if (!file.is_open()) {
cerr << "Unable to open file" << endl;
return 1;
}
getline(file, line);
tokenizer<> tok_head(line);
int n_columns = 0;
for (tokenizer<>::iterator beg=tok_head.begin(); beg!=tok_head.end(); ++beg) {
cout << *beg << '\t';
n_columns++;
}
cout << endl;
while (getline(file, line)) {
file_contents += line;
}
file.close();
tokenizer<> tok(file_contents);
int i = 0;
for (tokenizer<>::iterator beg=tok.begin(); beg!=tok.end(); ++beg, ++i) {
cout << *beg;
if (i % n_columns) {
cout << '\t';
} else {
cout << endl;
}
}
return 0;
}
Makefile
all: t
t: csv.cpp
g++ -I /usr/include/boost csv.cpp -o t
It looks like you must parse each line using different logic, so you should check first column first and using it apply appropriate logic, below is some pseudocode for that:
std::fstream fs("test.txt");
std::string line;
//
// Read line by line
while (std::getline(fs, line)) {
std::istringstream str(line);
std::string rec_type;
// Read record type (your first two lines looks like are of no type?)
if ( !std::getline(str, rec_type, ',') )
continue;
// Decide type of record, and parse it accordingly
if ( rec_type == "Transportation Cost") {
std::string val;
// Read comma delimited values
if ( !std::getline(str, val, ',') )
continue;
int ival1 = std::stoi(val);
if ( !std::getline(str, val, ',') )
continue;
int ival2 = std::stoi(val);
// ...
}
if ( rec_type == "Tariffs") {
std::string val;
if ( !std::getline(str, val, ',') )
continue;
int ival = std::stoi(val);
// ...
}
}
One method is to consider each line as a separate record and object.
Let the objects read their data.
For example:
class Tariff
{
int values[3];
public:
friend std::istream& operator>>(std::istream& input, Tariff& t);
};
std::istream& operator>>(std::istream& input, Tariff& t)
{
// Read and ignore the label "Tariff"
std::string name;
std::getline(input, name, ','); // Read until ',' delimiter.
input >> t.value[0];
// Note: the ',' is not a digit, so it causes an error state,
// which must be cleared.
input.clear();
input >> t.value[1];
input.clear();
input >> t.value[2];
input.clear();
}
Another method is to read the label first, then delegate to a function that reads in the row.
std::string row_text;
std::getline(text_file, row_text); // Read in first line and ignore.
while (std::getline(text_file, row_text))
{
std::istringstream text_stream(row_text);
std::string label;
std::getline(text_stream, label, ','); // Parse the label.
// Delegate based on label.
// Note: can't use switch for strings.
if (label == "Tariffs")
{
Input_Tariff_Data(text_stream);
}
else if (label == "ShippingType")
{
Input_Shipping_Type_Data(text_stream);
}
//...
} // End-while
The if-else ladder can be replaced by a lookup table that uses function pointers. Sometimes the table is easier to read.
typedef void (*P_Input_Processor)(std::istringstream& text_stream);
struct Table_Entry
{
char const * label;
*P_Input_Processor input_processor;
};
//...
const Table_Entry delegation_table[] =
{
{"Tariffs", Input_Tariff_Data},
{"ShippingType", Input_Shipping_Type_Data},
};
const unsigned int entry_quantity =
sizeof(delegation_table) / sizeof(delegation_table[0]);
// ...
std::string row_text;
std::getline(input_file, row_text); // Read and ignore first line.
while (std::getline(input_file, row_text))
{
// Create a stream for parsing.
std::istringstream text_stream(row_text);
// Extract label text
std::string label;
std::getline(text_stream, label, ',');
// Lookup label in table and execute associated function.
for (unsigned int index = 0; index < entry_quantity; ++index)
{
if (label == delegation_table[index].name)
{
// Execute the associated input function
// by derferencing the function pointer.
delegation_table[index](text_stream);
break;
}
}
}
An alternative to the lookup table is to use:
std::map<std::string, P_Input_Processor>
or
std::map<std::string, void (*P_Input_Processor)(std::istringstream&)>

C++ program hangs when using cin.getline()

I am making a program keeping track of different persons, which I try to read in from a file. I use a constructor that takes an ifstream file as an argument, and I then try to read in the data from the file. I can read the first line, which is just an int (a unique number for each person), but when I try to go to the next line and getline it, the program hangs. Does anyone know why?
#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
#include <cstdlib>
using namespace std;
const int MAXPERS = 100;
const int MAXTXT = 80;
const int DATELEN = 7;
class Person {
private:
int nr;
char* firstName;
char birthDate[DATELEN];
public:
Person() {
char fname[MAXTXT];
cout << "First name: "; cin.getline(fname, MAXTXT);
firstName = new char[strlen(fname) + 1];
strcpy(firstName, fname);
cout << "Birth date (DDMMYY): ";
cin >> birthDate; cin.ignore();
}
Person(int n, ifstream & in) {
nr = n;
char fname[MAXTXT];
cin.getline(fname, MAXTXT);
firstName = new char[strlen(fname) + 1];
strcpy(firstName, fname);
in >> birthDate;
}
void display() {
cout << "\nFirst name: " << firstName;
cout << "\nBorn: " << birthDate;
}
void writeToFile(ofstream & ut) {
ut << firstName << "\n" << birthDate;
}
};
void readFromFile();
Person* persons[MAXPERS + 1];
int lastUsed = 0;
int main() {
readFromFile();
persons[1]->display();
return 0;
}
void readFromFile() {
ifstream infile("ANSATTE.DAT");
if(infile) {
while(!infile.eof() && lastUsed < MAXPERS) {
int nr;
infile >> nr;
persons[++lastUsed] = new Person(nr, infile);
}
}
}
My file looks like this:
1
Andy
180885
2
Michael
230399
In your constructor you have
cin.getline(fnavn, MAXTXT);
So your program is waiting for you to type something in. If you meant to get the name from the file then you need
in.getline(fnavn, MAXTXT);
^^ ifstream object
You are also going to run into the issue of mixing >> with getline. You will need to add
infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
after infile >> nr; in your while loop.
strlen(fname + 1) will be strlen(fname) - 1 if fname is one-character long or more and indeterminate if fname is zero-character long. It should be strlen(fname) + 1.
strlen(fnavn + 1) has the same issue and should be strlen(fnavn) + 1.

how to insert data from a text file into an array of struct

I need to read data from a text file, and insert the data in an array of struct. The data file is in the following format:
productname price quantity
my main concern is to read the product name, which consist of one, and two words. Should I approach product name as a c-string or as a string literal?
any help appreciated
#include <iostream>
#include <fstream>
using namespace std;
const int SIZE = 15; //drink name char size
const int ITEMS = 5; //number of products
struct drinks
{
char drinkName[SIZE];
float drinkPrice;
int drinkQuantity;
};
int main()
{
//array to store drinks
drinks softDrinks[ITEMS];
//opening file
ifstream inFile;
inFile.open("drinks.txt");
char ch;
int count = 0; //while loop counter
if(inFile)
{
while(inFile.get(ch))
{
//if(isalpha(ch)) { softDrinks[count].drinkName += ch; }
//if(isdigit(ch)) { softDrinks[count].drinkPrice += ch; }
cout << ch;
}
cout << endl;
count++;
}
else
{
cout << "Error opening file!\n";
system("pause");
exit(0);
}
system("pause");
return 0;
}
Since you ask for "any help", here's my view: Forget everything you wrote, and use C++:
#include <fstream> // for std::ifstream
#include <sstream> // for std::istringstream
#include <string> // for std::string and std::getline
int main()
{
std::ifstream infile("thefile.txt");
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
std::string name;
double price;
int qty;
if (iss >> name >> price >> qty)
{
std::cout << "Product '" << name << "': " << qty << " units, " << price << " each.\n";
}
else
{
// error processing that line
}
}
}
You could store each line of data in a std::tuple<std::string, int, double>, for example, and then put those into a std::vector as you go along.