structure to files and back again - c++

I have a program that puts some strings and integers in a structure.
now I want to make it so if the program is opened it gets all information from the file and if the program is closed I want to save all info to the same file so I can find it the next time.
What is the best way to do that with one file to put it all in or a seperate file per each variable?
If I have figured that out I need to know how to find a specific line and than read the info from there. (like the name of line 59 goes to the 59th place in a structure array.),
then I have to overwrite certain info like the amount of games played and how many of them are won, lost or tied. (it is a little game.)
Here is the structure:
struct Recgame{
char name[20];
char surname[20];
int games; //needs to be overwritable in file
int won; //needs to be overwritable in file
int same; //needs to be overwritable in file
int lost; //needs to be overwritable in file
int place; //needs to be overwritable in file
int money; //needs to be overwritable in file
} info[100];

The C++ way to do this, is writing an stream-inserter and a stream-extractor for the struct Recgame.
The prototypes are:
std::ostream& operator<<( std::ostream& out, const Recgame& recgame );
std::istream& operator>>( std::istream& in, Recgame& recgame );
After this, You can easy write the infos to a file
ofstream file("afile.txt");
for( int i=0; i<n; ++i ) // n should be the number of the objects
file << info[i];
the implementation of the writing could be:
std::ostream& operator<<( std::ostream& out, const Recgame& recgame )
{
// make sure, that the char-arrays contain a closing char(0) -> ends
out << recgame.name << "\n";
out << recgame.surname << "\n";
out << recgame.games << " " << recgame.won << " " << recgame.same << " " <<
recgame.lost << " " << recgame.place << " " << recgame.money << "\n";
return out;
}
the implementation of the reading extractor
std::istream& operator>>( std::istream& in, Recgame& recgame )
{
in >> std::skipws; // skip leading spaces
in.getline( recgame.name, 20 ).ignore( std::numeric_limits< std::streamsize >::max(), '\n' ); // requires #include <limits>
in.getline( recgame.surname, 20 ).ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
in >> recgame.games >> recgame.won >> recgame.same >>
recgame.lost >> recgame.place >> recgame.money;
return in;
}
read it from file:
ifstream file("afile.txt");
int n = 0; // number of read objects
for( ; n < N && file >> info[n]; ++n ) // -> const int N = 100;
;
if( file.eof() )
cout << "Ok - read until end of file\n";
cout << "read " << n << " objects" << endl;

You can use fwrite function in C to write the structure in a binary file, and fread to read it back again.
If you want to use C++ style file I/O, then you need to overload << and >> operator.

Related

How do I write an array of contents into a text file? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
The code is supposed to open an existing text file, transfer the contents into the array, then create a new text file and then write the array contents into the new text file. The problem I'm having is that the code only outputs the last line of the content from the new text file.
file.open("Patient.txt", ios::in);
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB >> arr[i].address >> arr[i].Dr_name >> arr[i].V_date;
/*cout << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date << endl;*/
}
file.close();
files.open("Patients_2.txt");
if (files.is_open()) {
for (i; i < 30; i++) {
files << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date;
}
}
files.close();
patientss.open("Patients_2.txt");
cout << "Patient 2: " << endl;
while (!patientss.eof()) {
getline(patientss, line);
cout << line << endl;
}
patientss.close();
system("pause");
return 0;
}
IMHO, you should overload the formatted insertion and extraction operators in your patient class:
struct Patient
{
std::string name;
std::string dob;
std::string address;
std::string dr_name;
std::string v_date;
friend std::istream& operator>>(std::istream& input, Patient& p);
friend std::ostream& operator<<(std::ostream& output, const Patient& p);
};
std::istream& operator>>(std::istream& input, Patient& p)
{
std::getline(input, p.name);
std::getline(input, p.dob);
std::getline(input, p.address);
std::getline(input, p.dr_name);
std::getline(input, p.v_date);
return input;
}
std::ostream& operator<<(std::ostream& output, const Patient& p)
{
output << p.name << "\n";
output << p.dob << "\n";
output << p.address << "\n";
output << p.dr_name << "\n";
output << p.v_date << "\n";
return output;
}
The above makes input and output easier:
std::vector<Patient> database;
Patient p;
while (input_file >> p)
{
database.push_back(p);
}
const unsigned int quantity = database.size();
for (unsigned int i = 0; i < quantity; ++quantity)
{
output_file << database[i];
}
The above code also supports the concepts of encapsulation and data hiding. The Patient struct is in charge or reading its members because it knows the data types of the members. The code external to the Patient is only concerned with the input and output of a Patient instance (doesn't care about the internals).
This loop has a few problems:
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB ....
You never increase i so the same arr[i] will be overwritten time and time again.
You use !file.eof() as a condition to stop reading. eof() does not get set until after you've tried to read beyond the end of the file, which means that if you had increased i as you should, the last arr would be empty / broken. Instead check if the extraction from the stream succeeded. Since the stream is returned when you do stream >> var and has an overload for explicit operator bool() const which returns !fail() you can do this check directly in your loop:
while(stream >> var) { extraction success }
Using formatted input (>>) for string fields that are likely to contain spaces is however not a good idea. Your name, nung khual, would be split so nung would go into name and khual would go into DOB. It's better to use a field separator that is very unlikely to be included in anyone's name. \n is usually good and works well with std::getline.
std::getline returns the stream that you gave as an argument which means that you can chain getlines similarly to stream >> var1 >> var2, except it's a little more verbose.
getline(getline(stream, var1), var2) will put the first line in var1 and the second line in var2.
To make input and output a little simpler you can add stream operators for your data type and make the input stream operator use getline for your fields.
Example:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct data_t {
std::string name;
std::string DOB;
std::string address;
std::string Dr_name;
std::string V_date;
};
// input stream operator using chained getlines
std::istream& operator>>(std::istream& is, data_t& d) {
using std::getline;
return getline(getline(getline(getline(getline(is,
d.name), d.DOB), d.address), d.Dr_name), d.V_date);
}
// output stream operator
std::ostream& operator<<(std::ostream& os, const data_t& d) {
return os << d.name << '\n'
<< d.DOB << '\n'
<< d.address << '\n'
<< d.Dr_name << '\n'
<< d.V_date << '\n';
}
int main() {
std::vector<data_t> arr;
if(std::ifstream file("Patient.txt"); file) {
data_t tmp;
while(file >> tmp) { // remember, no eof() needed
arr.push_back(tmp);
}
}
if(std::ofstream file("Patients_2.txt"); file) {
for(const data_t& d : arr) {
file << d;
}
}
if(std::ifstream patientss("Patients_2.txt"); patientss) {
data_t tmp;
while(patientss >> tmp) {
std::cout << tmp;
}
}
}

Read from binary file > data into class object, missing data

I'm currently making a file that reads ASCII file to read binary file instead. It has to put data into an object named Arragements and Customers. However, something is missing. It does not read the first line of the files.
It is reading everything correctly (it seems), except the fact that it doesn't read the first line of the file at all, and instead it adds 5 zeros at the end when it is supposed to not be anything there. In other words, it is not reading everything. Any big mistakes made? I'm changing the code from reading a ASCII file to read a BINARY file instead. If it make any changes, I've added how the readTicketsFromFile looked like when it was written to read ASCII file blow.
Here's the code when reading from ASCII: https://pastebin.com/WswHzpxM
The file format is (10 lines total):
< nr (2) > < amount tickets(4) > < price(3) > < title(30) >
Here's what I ried to make, "translating" the code to read same file in BINARY (It is obviously converted to BINARY file):
#include <fstream> // ifstream, ofstream
#include <iostream> // cout
#include <cstring> // strcpy
#include <cstdlib> // (s)rand
using namespace std;
const int ARRLEN = 35;
// CLASSES
class Arrangement {
private:
char title[ARRLEN]; // Arrangement title/name
int price; // Price pr. ticket
int amountSpaces; // Amounts of spaces/tickets left
int amountSold; // Amounts of tickets sold so far
int totalTicketsOrdered; // Total amount of tickets
int totalCustomersOrdered; // Amount of customers wanting a ticket
public:
Arrangement();
void update(char titt[], int ant, int pri);
void updateAmountWishedTickets(int antBill);
void updateAmountSold(int ant);
bool needPull();
int amountLeft();
int amountOrdered();
void write()
{
cout
<< '\n' << price << "\t"
<< amountSpaces << "\t"
<< amountSold << "\t"
<< totalTicketsOrdered
<< "\t" << totalCustomersOrdered
<< '\t' << title;
}
};
// READ FROM FILE 1
void readTicketsFromFile() {
char title[ARRLEN];
int nr, amount, price; // Variables 3 first "spaces" of the file
int size = 0;
// post/linje på filen.
ifstream infile;
infile.open("tickets.res", ios::in | ios::binary);
if (infile.is_open()) {
infile.seekg(0, ios::end);
size = (int)infile.tellg() / sizeof(Arrangement);
cout << "\n# in file: " << size << endl; // Prints out: 10
// Read from file, supposed to read everything from the file
for (int i = 1; i <= size; i++) {
infile.seekg(i * sizeof(Arrangement));
infile.read((char *)& arrangementer[i], sizeof(Arrangement));
cout << '\n' << i << " read: \n";
arrangementer[i].write(); // Used to see what what's inside the object, 1. missing.
}
}
else cout << "Couldn't open the file";
infile.close();
}
int main() {
readTicketsFromFile();
return 0;
}
What it shoud look like:
https://i.gyazo.com/69c5560766c33ebc15cac25e13b4de72.png
What it looks like:
https://i.gyazo.com/a9081f65ff629f3b57cb2c20750087b5.png

istringstream not storing anything in variables

I'm having an issue with istringstream not storing the values it reads. Here is what I have:
if(inputFile.good()){ //Make sure file is open before trying to work with it
//Begin Working with information
cout << "\tIn File: " << input << endl;
cout << "------------------------------------" << endl;
int number_of_lines = 0;
std::string line;
while (std::getline(inputFile, line)){
++number_of_lines;
}
Time times[number_of_lines];
double math[number_of_lines];
std::string input;
int hh, mm;
for(int loop=0;loop<number_of_lines;loop++){
std::getline(inputFile, input);
std::istringstream(input) >> mm >> hh >> math[loop];
cout << "hours = " << hh << endl;
times[loop].setTimeHours(hh);
times[loop].setTimeMinutes(mm);
times[loop].show();
cout << "*" << math[loop] << endl;
}
std::cout << "Number of lines in text file: " << number_of_lines << "\n" << endl;
}else{
cout << "Could not open file!!!" << endl;
}
The file I'm reading looks like this:
90 1 3.0
1 1 100.0
2 34 5.1
And the output when I run:
In File: data04.txt
------------------------------------
hours = 0
Operation To Be Done = 0:2336552*1.15384e-317
hours = 0
Operation To Be Done = 0:2336552*1.58101e-322
hours = 0
Operation To Be Done = 0:2336552*1.15397e-317
Number of lines in text file: 3
Anyone know why its not storing the values?
There are several key problems in this code
It doesn't check if inputs are successful. You always need to make sure you verify that the input operations worked before you process the data you read. Failing so will cause random data to be processed.
You first read to the end of the stream and then hope that the stream magically restarted. That won't work. Read the stream just once and keep appending to a std::vector<Time> (or similar container). Aside from only traversing the file once, on UNIXes the file size can change while reading.
C++ doesn't have variable sized arrays although some compiler may offer an extension similar to C's variable sized array. In C++ you'd use a std::vector<Time> instead.
First and foremost, your program is wrong. After the while loop ends, there is nothing more to read in the file (unless you seekg() back to the beginning), so the std::getline() call in the for loop body basically does nothing.
A second problem is that concerns are not properly separated.
Here is how I would have implemented this program:
struct line_data
{
Time t;
double x;
};
// This handles reading a single Time value.
std::istream & operator >> (std::istream & is, Time & t)
{
int hh, mm;
if (is >> hh >> mm)
{
// Not happy with the following two lines, too Java-like. :-(
t.setTimeHours(hh);
t.setTimeMinutes(mm);
}
return is;
}
// This handles reading a single line of data.
std::istream & operator >> (std::istream & is, line_data & ld)
{
std::string s;
if (std::getline(is, s))
{
std::istringstream iss(s);
// Ensure errors are propagated from iss to is.
if (!(iss >> ld.t >> ld.x))
is.setstate(std::ios::failbit);
}
return is;
};
// This handles processing a single line of data.
struct line_manip // satisfies concept OutputIterator<line_data>
{
std::back_insert_iterator<std::vector<Time>> ti;
std::back_insert_iterator<std::vector<double>> xi;
line_manip(std::vector<Time> & ts, std::vector<double> & xs)
: ti(std::back_inserter(ts))
, xi(std::back_inserter(xs))
{
}
line_manip & operator = (const line_data & ld)
{
ti = ld.t;
xi = ld.x;
return *this;
}
line_manip & operator * () { return *this; }
line_manip & operator ++ () { return *this; }
line_manip & operator ++ (int) { return *this; }
};
int main()
{
std::ifstream ifs("input.txt");
std::vector<Time> ts;
std::vector<double> xs;
std::copy(std::istream_iterator<line_data>(ifs),
std::istream_iterator<line_data>(),
line_manip(ts, xs));
// ...
}

Program not counting number of records [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I wrote a program that reads to one file, writes to another and prints to the screen. In other words, it is supposed to do three things (i.e. read, write and print).
The program is also supposed to count the number of records in a file named "Chap_11_employee_data.txt". It is just displaying the following, however.
**Total Records 0**
The End
When it writes to a file, it also displays weird numbers such as -858993460. I have tried almost everything, which led me to register an account here. My code is listed below as well as the file I am trying to read from and the file I am trying to write to.
Thank you very much for your guy's time.
Code:
#include <iostream>
#include <fstream>
#include <iomanip>
using std::cin;
using std::cout;
using std::endl;
using std::setw;
using std::ios;
using std::ifstream;
using std::ofstream;
const int EMPLOYEES = 20;
const int MAX = 21;
int ReadData( ifstream &inFile, ofstream &outFile, char name[][MAX], int age[] );
void WriteOutputFile( ofstream &outFile, char name[ ][MAX], int age[ ], int counter );
void PrintTotalsAndSummary( ofstream &out, int totalRecords );
int main()
{
char name[EMPLOYEES][MAX];
int age[EMPLOYEES];
int record_counter(0);
ifstream inFile;
ofstream outFile( "Chap_11_Report.txt" );
inFile.open( "Chap_11_employee_data.txt" );
if ( inFile.is_open() )
{
record_counter = ReadData( inFile, outFile, name, age );
inFile.close();
if( outFile.is_open() )
{
WriteOutputFile( outFile, name, age, record_counter );
PrintTotalsAndSummary( outFile, record_counter );
outFile.close();
}
else
{
cout << "Trouble Opening File";
cout << "\n\n\t\t ** About to EXIT NOW! ** ";
}
}
else
{
cout << "Trouble Opening File";
cout << "\n\n\t\t ** About to EXIT NOW! ** ";
}
return 0;
}
int ReadData( ifstream & inFile, ofstream & outFile, char name[][MAX], int age[] )
{
int counter = 0;
inFile >> name[counter] >> age[counter]; // Priming Read
while ( !inFile.eof() )
{
cout << setiosflags( ios::left ) << setw( 25 )
<< name[counter] << resetiosflags( ios::left )
<< setw( 4 ) << age [counter] << endl;
counter++;
inFile >> name[counter] >> age[counter];
}
return counter;
}
void WriteOutputFile( ofstream &outFile, char name[][MAX], int age[], int counter)
{
outFile << " Here is the Output File" << endl;
for ( int r = 0; r <= counter; r++ )
{
outFile << setiosflags( ios::left ) << setw( 25 )
<< name[r] << setw( 4 )
<< resetiosflags( ios::left ) << age[r]
<< endl;
}
}
void PrintTotalsAndSummary( ofstream &outFile, int totalRecords )
{
// To screen
cout << "\n\n\t** Total Records: " << totalRecords << " **\n"
<< "\t\t The End \n";
// To file
outFile << "\n\n\t** Total Records: " << totalRecords << " **\n"
<< "\t\t The End \n";
}
File I am reading from (Chap_11_employee_data.txt):
"Alexis","Blough",1-1921,"CEO"
"Bill","Pay",1-7711,"Accounting"
"Willy","Makit",4-1595,"Sales"
"Marie","Murray",1-4986,"MIS"
"Cal","Caldwellowinski",5-0911,"MIS"
"Jamie","Johanasola",5-9999,"Marketing"
File I am writing to (Chap_11_Report.txt):
Here is the Output File
-858993460
** Total Records: 0 **
The End
Here is your input statement...
inFile >> name[counter] >> age[counter];
And the output file format is this...
"Alexis","Blough",1-1921,"CEO"
"Bill","Pay",1-7711,"Accounting"
The input statement reads the name till it encounters a space or an endline character. So in the first case it would read the whole line as there is no space in between the words. Next on, it would take input of age which is an int. It would start taking input of age from the second line, which is full of characters from the beginning. A character in integer stream is undefined behavior. That is the reason you are getting wrong results.
The problem is the mismatch of the file format with the input format. Try to synchronise them.
For reading white spaces as well, use inFile.get() or inFile.getline().
Your read has one problem:
while ( !inFile.eof() )
The problem is that the stream can go bad in a way that is not eof(). If this happens you will enter an infinite loop. So really you should check for bad().
while ( !inFile.bad() ) // check for eof and other error cases.
Normally this test is completely wrong. But you have managed to get around this by doing the following:
<Get Input>
while ( !inFile.bad() )
{
< DO WORK>
<Get Input>
}
The same affect can be achieved (in a more idiomatic way) with:
while ( <Get Input> )
{
< DO WORK>
}
The read (using >> or std::getline()) returns a reference to a stream. When this is used in a boolean context (like a while loop test) it is converted to bool (actually bool like for the language lawyers) that represents the state of the stream which is obtained by using bad. Thus the loop body is only entered if the read actually worked.
The input stream operators are very simplistic and any failure on input will set an internal failure bit inside the stream causing them to sieze up and not provide further input until the failure bit is reset.
Given the input you have provided this will fail:
inFile >> name[counter] >> age[counter];
The variable name[counter] represents a C-String and the the input operator >> when applied to a C-String will read until the first white space character (in this case newline). So here we assign:
"Alexis","Blough",1-1921,"CEO"
to this value.
We then try and continue reading using age[counter] an integer value. This tries to read an integer from:
"Bill","Pay",1-7711,"Accounting"
This will fail as the character '"' is not a digit.
What you should be doing is:
std::string name;
std::getline(inFile, name, ','); // Read upto the ',' character (and discard it)
// You may want to remove the quotes here.
int age = 0;
// Age is not defined in the input so set it manually.
std::string line;
std::getline(inFile, line); // Read and ignore the rest of the line.

Read a file twice in C++ because of eof?

I read numbers from a file, apply 3 functions and print out to another file:
int main(int argc, char** argv) {
std::ifstream fin;
fin.open("input.txt");
std::ofstream fout;
fout.open("output.txt", std::ios::app);
char arr[50];
int a,b;
int N;//number to factor
while (!fin.eof()){
//Print backward
fin >> arr;
PrintBackward( arr );
fout << endl;
//Greatest common divisor
((fin >> a) >> b);
fout << gcd( a, b );
fout << endl;
//Find prime factor
fin >> N;
PrimeFactor(N);
fout << endl;
}
fin.close();
fout.close();
return 0;
}
After running, the result is duplicated:
olleh
3
2 3 7
olleh
3
2 3 7
I read a similar article but it's about reading into 1 variable so it seems not to be feasible.
If I set a break at the end of the while loop, it's fine. Is there any way not to use break?
while (!whatever.eof()) is essentially always wrong, and will never detect the end of the file correctly. In your case, it's easiest to coalesce the reads together, and then do all the processing, something like this:
while (fin >> arr >> a >> b >> N) {
PrintBackwards(arr);
fout << "\n";
fout << gcd(a, b) << "\n";
fout << PrimeFactor(N) << "\n";
}
The crucial part is to check the result of the read, instead of checking and reading separately from each other.
A couple more bits of advice: I'd use an std::string instead of an array. I'd also separate reversing the string from printing it, so you can have something like:
fout << reverse(arr) << "\n"
<< gcd(a, b) << "\n"
<< PrimeFactor(N) << "\n";
Emphasizing the commonality between the operations tends to be a good thing.
Edit: Just for fun, I'll point out another way you could do things if you wanted. Since you're basically reading and processing the four items as a group, you could make that grouping a bit more explicit:
struct item {
std::string arr;
int a, b, N;
friend std::istream &operator>>(std::istream &is, item &i) {
return is >> arr >> a >> b >> N;
}
};
struct process {
std::string operator()(item const &i) {
std::ostringstream buffer;
buffer << reverse(arr) << "\n" << gcd(a, b) << "\n" << PrimeFactor(N);
return buffer.str();
}
}
With this, you can let the standard library deal with all the details of the reading and writing, checking end of file, etc.:
std::transform(std::istream_iterator<item>(fin),
std::istream_iterator<item>(),
std::ostream_iterator<std::string>(std::cout, "\n"),
process());
My guess is that you're checking eof too early - it's only set when you try to read and the read fails because you're at the end of the file. Try adding this after fin >> arr:
if (fin.eof()) break;
Actually you should be checking for errors after every IO operation - not to do so is sloppy coding and won't be robust.