how can i read file line by line for c++ - c++

beginner in c++ here...
im making a multiple txt file to being read, it is testAnswers and CorrectAnswers
inside testAnswers is:
S1234 A B D C C C D A D D
S2345 A C D A A B D C A A
S3456 C A A A C C D D B B
S4567 A B B B B A B C C D
S5678 B C C B A A B C C D
S6789 A B B C C A B A C C
S7890 C B A A D A C A A C
S8912 A D A D A C C B A C
S9123 B C C A A C D B B A
S1928 B C C A A B A D B B
S2938 A B D C B D B D A A
S3847 C B D C C C B D A B
inside CorrectAnswers is:
A C D A A B B D C D
here the code i do
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
ifstream inputStream1, inputStream2;
ofstream outputStream;
inputStream1.open("testAnswers.txt");
inputStream2.open("CorrectAnswers.txt");
outputStream.open("outputAnswers.txt");
string name, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1;
string a2, b2, c2, d2, e2, f2, g2, h2, i2, j2;
string id;
while(inputStream1 >> name >> a1 >> b1 >> c1 >> d1 >> e1 >> f1 >> g1 >> h1 >> i1 >> j1){
}
while(inputStream2 >> a2 >> b2 >> c2 >> d2 >> e2 >> f2 >> g2 >> h2 >> i2 >> j2){
}
cout << "id: ";
cin >> id;
if(id == name){
if(a1==a2){
num+=1;
}
if(b1==b2){
num+=1;
}
if(c1==c2){
num+=1;
}
if(d1==d2){
num+=1;
}
if(e1==e2){
num+=1;
}
if(f1==f2){
num+=1;
}
if(g1==g2){
num+=1;
}
if(h1==h2){
num+=1;
}
if(i1==i2){
num+=1;
}
if(j1==j2){
num+=1;
}
}
cout << num << endl;
outputStream << num << endl;
inputStream1.close();
inputStream2.close();
outputStream.close();
return 0;
}
ABCD is an answer. example, User S1234 is answers A B D C C C D A D D. if we compared CorrectAnswers, he got 3 correct
thats what it should be happen. but the code only can read the last User only, S3847. im using while(input) because im assume it will read line by line but it not.
how can i make the code read each line from above first?

Let me try to help you.
First we will look at your program and comment on it.
using namespace std;
you should not open the complete standard namespace. You will find many many comments here on SO recommending that. So, please omit that line an use qualified names everywhere (like std::string)
ifstream inputStream1;
inputStream1.open("testAnswers.txt");
You can and should do this in one line. The iostreams have a constructor, taking the filename. Additionally, you should always check the result of any IO operation. This you can simply do with an if (inputStream1), because the bool and the !-operator for iostreams are overloaded and return the state of stream. And, you should not use hardcoded string literals, please use const std::strings instead and store the file name. Last, but not least, we will use the if-statement with initializer to prevent the pollution of the outer name space. You should always try to narrow the scopy of your variable as much as possible. The resulting statement for opening a file will be:
if (std::ifstream correctAnswersStream{ fileNameCorrectAnswers }; correctAnswersStream) {
Another big advantage is that the iostreams destructor will close the file automatically for you, when they "fall" out of scope. So, no need for a close-statement.
Then you define tons of string variables. First strong recommendation: Please always initialize all variables. Use the {} for default initialization. Ant, if you have many variables with the same meaning, you should put them in a ```std::vector. If you have variables that logically belong together, the put them in a struct(class````). for example like:
std::vector<std::string> answer{};
of for a struct something like that:
struct Test {
std::string name{};
std::vector<std::string> answer{};
};
And then you would define methods to operate on your data. At least you could overwrite the inserter (<<) and the extractor (>>) operator for simple io operations. Only the class (struc) should know, how to read write data, not the outer world.
So you could for example add a simple inserter like the belo:
struct Correct {
std::vector<std::string> answer{};
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Correct& c) {
std::copy(c.answer.begin(), c.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
Now let's look at you next statement and the main source of your problems:
while(inputStream1 >> name >> a1 >> b1 >> c1 >> d1 >> e1 >> f1 >> g1 >> h1 >> i1 >> j1){
}
You use a while-loop to read you variables, but, the problem is that you do nothing in the loop body. YOu read the variables, but not store their value in a std::vector or somehwere else. And then, during the next loop run, all preiviously read values will be overwritten with the new vales and their old content will be lost. And when the loop finishes, you will have the values of the last line in your varaibles, because you have overwritten and discarded all previously read values. That needs to changed.
Store the contents of the variables somewhere.
This you need to do for both input files. Then you can compare verything with everything else.
You will find out that you need somehow a 2 dimensional container. Or, a std::vector for your own struct which again contains another std::vector.
Because I want to help you, but not do your homework, I will add an example program using a little bit more sophisticated coding style. With that you can understand the logic, but most certainly not some of the more advanced stuff.
However, of course the following will work:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
struct Correct {
std::vector<std::string> answer{};
friend std::istream& operator >> (std::istream& is, Correct& c) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Put line into an istringstream for easier extraction
std::istringstream iss(line);
// CLear all old data from vector
c.answer.clear();
// Copy all answers from file into internal vector
std::copy(std::istream_iterator<std::string>(iss), {}, std::back_inserter(c.answer));
}
return is;
}
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Correct& c) {
std::copy(c.answer.begin(), c.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
struct Test {
std::string name{};
std::vector<std::string> answer{};
friend std::istream& operator >> (std::istream& is, Test& t) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Put line into an istringstream for easier extraction
std::istringstream iss(line);
// Try to extract the name
if (iss >> t.name) {
// Clear all old data from vector
t.answer.clear();
// Copy all answers from file into internal vector
std::copy(std::istream_iterator<std::string>(iss), {}, std::back_inserter(t.answer));
}
}
return is;
}
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Test& t) {
std::copy(t.answer.begin(), t.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
const std::string fileNameTestAnswers{ "r:\\testAnswers.txt" };
const std::string fileNameCorrectAnswers{ "r:\\CorrectAnswers.txt" };
const std::string fileNameResult{ "r:\\outputAnswers.txt" };
int main() {
// Open file with test answers and check, if it could be opened
if (std::ifstream testAnswersStream{ fileNameTestAnswers }; testAnswersStream) {
// Open file with correct answers and check, if it could be opened
if (std::ifstream correctAnswersStream{ fileNameCorrectAnswers }; correctAnswersStream) {
// Open file for the resulting output
if (std::ifstream resultAnswersStream{ fileNameCorrectAnswers }; resultAnswersStream) {
// Now all the files are open and we can start. Try to read all files
std::vector<Correct> correct{};
std::vector<Test> test{};
// Read files
std::copy(std::istream_iterator<Correct>(correctAnswersStream), {}, std::back_inserter(correct));
std::copy(std::istream_iterator<Test>(testAnswersStream), {}, std::back_inserter(test));
// Inform user that he has to enter a name
std::cout << "\nPlease enter a name for checking the results:\n";
// Read the name
if (std::string searchName{}; std::getline(std::cin, searchName)) {
// Search the input search name in our test vector
// Goto all tests, because maybe the name is in the file several times
for (auto record{ std::find_if(test.begin(), test.end(), [&searchName](const Test& t) { return t.name == searchName; }) };
record != test.end();
record = std::find_if(std::next(record), test.end(), [&searchName](const Test& t) { return t.name == searchName; })) {
// So go through all the records that contain the search names and compare with all answers
for (const auto& co : correct) {
size_t okCounter{};
for (size_t r{}, c{}; r < record->answer.size() && c < co.answer.size(); ++r, ++c) {
if (record->answer[r] == co.answer[c]) ++okCounter;
}
// Incase there was a match, show result
if (okCounter > 0U)
std::cout << "\nName:\t\t " << searchName << "\nGiven answer:\t " << *record << "\nCorrect answer:\t "
<< co << "\nNumber matches:\t " << okCounter;
std::cout << "\n\n";
}
}
}
else std::cerr << "\n\nError while reading search name files\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameResult << "'\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameCorrectAnswers << "'\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameTestAnswers << "'\n\n";
return 0;
}

Related

Delete n precedent line from file

I have a file, that contain student name, id, age and number of this student. I want to read the file and for example if the number of student is 2 delete all 3 precedent lines corresponding to this student.
My txt look like this
Marc 45646564 18 1 Jean 4563213 21 2
Paul 45654 22 4 Alice 45948 20 5
while (getline(student, line))
{
if (counter % 4 == 2) { // Here I want to delete all prcedent 3 lines}
}
Unfortunately your approach, or your design idea is not that good. It should be improved a little bit.
You need to start thinking in an object-oriented way. The object "student" has properties or data elements, like "name", "id", "age" and "number". All this data belong together and are related to a specific "student". And next, you need to define methods that should operate on this data, like "reading" and "writing" the complete data for some student.
If the "number" property that you have, after reading a complete "student" record, is ok, then you will store it in some kind of array or std::vector
So, lets start with the data part:
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
};
This will describe the properties of one student.
Next, we do want to operate on this data. And the first operation is that we do want to read this data from any kind of stream, for example from the console (std::cin) or from a file (std::ifstream). This basically doesn't matter, stream is stream :-)
The iostream library has the so called "extractor" operator for reading from streams. That is the >> operator. And this we can simply define for our "Student" struct. Like so:
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
The signature of the function is given. This is a formal requirement. Then, now to what are we doing in that function. I would like to remind everybody that the istream extraction operations always return a reference to the given istream.. So, first we call std::getline. This will read the name of the student, and then return again is. Then the line looks like return is >> s.id >> s.age >> s.number. Next we will read the id. This will also return is. Then the statement would look like return is >> s.age >> s.number. And so on and so on. At the end we will have read everything and simply return is;
In the std::getline you will see >>std::ws. This will ignore all leading white spaces like new lines or whatever.
So, now, if we have an open file stream, let' s say "ifs" and a Student variable called "student", then we can write ifs >> student and it will read a complete student record from the file. So, all 4 lines.
By the way, we can do the same mechanism for the inserter function <<.
If we run this in a loop now and store the read student data into a std::vector we can copy all data from the file.
Or, if a student has a certain property, then we can also decide to not copy the data.
This could then look like:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
friend std::ostream& operator << (std::ostream& os, const Student& s) {
return os << s.name << '\n' << s.id << '\n' << s.age << '\n' << s.number << '\n';
}
};
int main() {
// Here we will store all our studends
std::vector<Student> students{};
// Open the file with the data
std::ifstream ifs("r:\\data.txt");
// Check, if the file could be opened.
if (ifs) {
Student tempStudent{};
// Read all students from a file, until we hit eof or some other problem
while (ifs >> tempStudent) {
// if the student does fullfill the requirements, then we take it, else not
if (tempStudent.number != 2)
students.emplace_back(std::move(tempStudent));
}
// OK, for debug purposes we show all data ion the screen
for (const Student& s : students) std::cout << s << '\n';
}
else std::cerr << "\n\nError: source file could not be opened\n\n";
return 0;
}
And if you want to see a more advance C++ solution, please look at the below:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
friend std::ostream& operator << (std::ostream& os, const Student& s) {
return os << s.name << '\n' << s.id << '\n' << s.age << '\n' << s.number << '\n';
}
};
int main() {
// Here we will store all our studends
std::vector<Student> students{};
// Open the file with the data and check, if it is open
if (std::ifstream ifs("r:\\data.txt");ifs) {
// Read all data
std::copy_if(std::istream_iterator<Student>(ifs), {}, std::back_inserter(students), [](const Student& s) {return s.number != 2;});
// For debug purposes we show all data ion the screen
std::copy(students.begin(), students.end(), std::ostream_iterator<Student>(std::cout, "\n"));
}
else std::cerr << "\n\nError: source file could not be opened\n\n";
return 0;
}

How to read CSV data to pointers of struct vector in C++?

I want to read a csv data to vector of struct in cpp, This is what I wrote, I want to store the iris dataset in pointer of struct vector csv std::vector<Csv> *csv = new std::vector<Csv>;
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv{
float a;
float b;
float c;
float d;
std::string e;
};
int main(){
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream *myFile = new std::ifstream("iris.csv");
std::vector<Csv> *csv = new std::vector<Csv>;
std::string line;
// Read the column names
if(myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while(std::getline(ss, colname, ',')){
std::cout<<colname<<std::endl;
}
}
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
return 0;
}
I dont know how to implement this part of the code which outputs line with both float and string.
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
Evolution
We start with you program and complete it with your current programm style. Then we analyze your code and refactor it to a more C++ style solution. In the end we show a modern C++ solution using more OO methods.
First your completed code:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream* myFile = new std::ifstream("r:\\iris.csv");
std::vector<Csv>* csv = new std::vector<Csv>;
std::string line;
// Read the column names
if (myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while (std::getline(ss, colname, ',')) {
std::cout << colname << std::endl;
}
}
// Read data, line by line
while (std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
// Extract each column
std::string column;
std::vector<std::string> columns{};
while (std::getline(ss, column, ',')) {
columns.push_back(column);
}
// Convert
Csv csvTemp{};
csvTemp.a = std::stod(columns[0]);
csvTemp.b = std::stod(columns[1]);
csvTemp.c = std::stod(columns[2]);
csvTemp.d = std::stod(columns[3]);
csvTemp.e = columns[4];
// STore new row data
csv->push_back(csvTemp);
}
// Show everything
for (const Csv& row : *csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
return 0;
}
The question that you have regarding the reading of the columns from your Csv file, can be answered like that:
You need a temporary vector. Then you use the std::getline function, to split the data in the std::istringstream and to copy the resulting substrings into the vector. After that, we use string conversion functions and assign the rsults in a temporary Csv struct variable. After all conversions have been done, we move the temporary into the resulting csv vector that holds all row data.
Analysis of the program.
First, and most important, in C++ we do not use raw pointers for owned memory. We should ven not use new in most case. If at all, std::unique_ptrand std::make_unique should be used.
But we do not need dynamic memory allocation on the heap at all. You can simply define the std::vector on the functions stack. Same like in your line std::string colname; you can also define the std::vector and the std::ifstream as a normal local variable. Like for example std::vector<Csv> csv{};. Only, if you pass this variable to another function, then use pointers, but smart pointers.
Next, if you open a file, like in std::ifstream myFile("r:\\iris.csv"); you do not need to test the file streams condition with if (myFile->good()). The std::fstreams bool operator is overwritten, to give you exactly this information. Please see here.
Now, next and most important.
The structure of your source file is well known. There is a header with 5 elements and then 4 doubles and at then end a string without spaces. This makes life very easy.
If we would need to validate the input or if there would be spaces within an string, then we would need to implement other methods. But with this structure, we can use the build in iostream facilities. The snippet
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
will do the trick. Very simple.
So, the refactored solution could look like this:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::vector<Csv> csv{};
std::ifstream myFile("r:\\iris.csv");
if (myFile) {
if (std::string header{}; std::getline(myFile, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
}
return 0;
}
This is already much more compact. But there is more . . .
In the next step, we want to add a more Object Oriented approch.
The key is that data and methods, operating on this data, should be encapsulated in an Object / class / struct. Only the Csv struct should know, how to read and write its data.
Hence, we overwrite the extractor and inserter operator for the Csv struct. We use the same approach than before. We just encapsulate the reading and writing in the struct Csv.
After that, the main function will be even more compact and the usage is more logical.
Now we have:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << '\t' << c.b << '\t' << c.c << '\t' << c.d << '\t' << c.e << '\n';
}
};
int main() {
std::vector<Csv> csv{};
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
if (std::string header{}; std::getline(myFileStream, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
while (myFileStream >> tmp)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row;
}
return 0;
}
OK. Alread rather good. Bit there is even more possible.
We can see that the source data has a header and then Csv data.
Also this can be modelled into a struct. We call it Iris. And we also add an extractor and inserter overwrite to encapsulate all IO-operations.
Additionally we use now modern algorithms, regex, and IO-iterators. I am not sure, if this is too complex now. If you are interested, then I can give you further information. But for now, I will just show you the code.
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <regex>
#include <iterator>
const std::regex re{ "," };
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
// Overwrite extratcor for simple reading of data
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
// Ultra simple inserter
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << "\t\t" << c.b << "\t\t" << c.c << "\t\t" << c.d << "\t\t" << c.e << '\n';
}
};
struct Iris {
// Iris data consits of header and then Csv Data
std::vector<std::string> header{};
std::vector<Csv> csv{};
// Overwrite extractor for generic reading from streams
friend std::istream& operator >> (std::istream& is, Iris& i) {
// First read header values;
if (std::string line{}; std::getline(is, line))
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(i.header));
// Read all csv data
std::copy(std::istream_iterator<Csv>(is), {}, std::back_inserter(i.csv));
return is;
}
// Simple output. Copy data to stream os
friend std::ostream& operator << (std::ostream& os, const Iris& i) {
std::copy(i.header.begin(), i.header.end(), std::ostream_iterator<std::string>(os, "\t")); std::cout << '\n';
std::copy(i.csv.begin(), i.csv.end(), std::ostream_iterator<Csv>(os));
return os;
}
};
// Driver Code
int main() {
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
Iris iris{};
// Read all data
myFileStream >> iris;
// SHow result
std::cout << iris;
}
return 0;
}
Look at the main function and how easy it is.
If you have questions, then please ask.
Language: C++17
Compiled and tested with MS Visual Studio 2019, community edition

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

Reading .txt file into array of struct

I'm a beginner in programming and i'm trying to read my .txt file into an array of struct in this program which after that display the data and then sort it, but the program only reads the first line and the loop won't stop until arraysize.
The file data looks like this:
ID NAME ADDRESS AGE
The Code:
#include <iostream>
#include <fstream>
#include <string>
#include <conio.h>
using namespace std;
struct bio
{
char name[50], address[50];
int id, age;
};
int main()
{
int i = 0, arraysize = 1000;
bio b[arraysize];
fstream data;
data.open("biodata.txt");
while(data.read((char*)&b, sizeof(b[i])))
{
for (i = 1; i < 1000; i++)
{
data >> b[i].id >> b[i].name >> b[i].address >> b[i].age;
}
}
for (i = 0; i < 1000; i++)
{
cout << b[i].id << " " << b[i].name << " " << b[i].address << " " << b[i].age << " " << endl;
}
data.close();
getch();
}
#include <iostream>
#include <fstream>
#include <string>
#define ARRAY_SIZE 1000
#define FILE_NAME "biodata.txt"
using namespace std;
struct Bio
{
int m_id;
string m_name;
string m_address;
int m_age;
};
int main()
{
Bio bio[ARRAY_SIZE];
ifstream data;
data.open(FILE_NAME);
if (!data)
{
cout << "not file " << FILE_NAME;
return 0;
}
for (int i = 0; i < ARRAY_SIZE && data.good(); ++i)
{
data >> bio[i].m_id >> bio[i].m_name >> bio[i].m_address >> bio[i].m_age;
}
for (int i = 0; i < ARRAY_SIZE ; ++i)
{
cout << bio[i].m_id << " " << bio[i].m_name << " " << bio[i].m_address << " " << bio[i].m_age << " " << endl;
}
data.close();
}
a few comments:
for what conio lib?
struct (bio) start with capital letter
don't use in char array in c++, you have string for this.
separate the variables to separate lines (bot "char name[50], address[50];")
better to rename members to m_X
about your "arraysize". if it const number you decide, do it with #define. if you need the whole file, you don't need it at all. (the file name too)
ifstream and not fstream data. you need just read. you don't want to change your data with some mistake.
check it the file opened well
in your code you check the while just before the loop.
in your condition loop check data.good(). it check it not eof and he file is readable.
read command is for binary file
it's better to separate the load file and print data to 2 differents functions. I didn't do it for save on your template
The following is maybe a little complicated for beginners, but since we are talking about C++, we should look also to a "more" objective oriented approach.
You designed a class, called bio. In object oriented languages you will put all data for an object and also all functions that operate on this data in the class. So you need to add member functions. The idea is that you encapsulate all data in an object. The outside world should not know anything about the details of the class. You just access it via member functions. And if you want to make changes later than you will do this within the member functions of the classes. And the rest of the program will continue to work.
Additionally we should definitely use C++ language features. For examples you should use std::string for strings and not Plain old C-Style char arrays. You should basically never use C-Style arrays in C++. Instead, please use STL container.
So, then let's design a class with data members and member functions. Since at the moment we just need input and output functionality, we overwrite the inserter and extractor operator. These operators know abot the data and behaviour of the class and will take care.
See the following program:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <sstream>
struct Bio
{
// Data
unsigned int id{};
std::string name{};
std::string address{};
unsigned int age{};
// Overload extractor operator to read all data
friend std::istream& operator >> (std::istream& is, Bio& b) {
std::string textLine{};
if (std::getline(is, textLine)) {
std::istringstream textLineStream{textLine};
textLineStream >> b.id >> b.name >> b.address >> b.age;
}
return is;
}
// Overload inserter operator to print the data
friend std::ostream& operator << (std::ostream& os, const Bio& b) {
return os << b.id << " " << b.name << " " << b.address << " " << b.age;
}
};
std::istringstream sourceFile{R"(1 John Address1 31
2 Paul Address2 32
3 Ringo Address3 33
4 George Address4 34
)"};
int main()
{
// Define Variable and read complete source file
std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
// Sort the guys by name
std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
// Show output on screen
std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
return 0;
}
Some comments. On StackOverflow, I cannot use files. So in my example program, I use a std::istringstream instead. But this is also an std::istream. You can use any other std::istream as well. So if you define an `````std::ifstreamto read from a file, then it will work in the same way as thestd::istringstream````.
And please see. The extractor operator does the whole work of reading the source File. It is encapsulated. No outside function needs to know, how it does.
In the main function, we define a std::vector and use its range contructor to specifiy where the data comes from. We give it the std::istream_iterator, which iterates over the input data and calls the extractor operator until verything is read.
Then we sort by names and copy the result to the output.
You may notice that fields in your input data are separted by space. This does in general not work for none atomic data. The name could exist of 2 parts and the address can have a street and a city. For this CSV (Comma separated Values) files have been invented.
Please see a more realistic soultion below.
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <sstream>
#include <regex>
struct Bio
{
// Data
unsigned int id{};
std::string name{};
std::string address{};
unsigned int age{};
// Overload extractor operator to read all data
friend std::istream& operator >> (std::istream& is, Bio& b) {
std::string line{};
std::regex re{";"};
if (std::getline(is, line)) {
std::vector<std::string> token{std::sregex_token_iterator(line.begin(), line.end(), re, -1), std::sregex_token_iterator()};
if (4 == token.size()) {
b.id = std::stoul(token[0]);
b.name = token[1];
b.address = token[2];
b.age = std::stoul(token[3]);
}
}
return is;
}
// Overload inserter operator to print the data
friend std::ostream& operator << (std::ostream& os, const Bio& b) {
return os << b.id << ", " << b.name << ", " << b.address << ", " << b.age;
}
};
std::istringstream sourceFile{R"(1; John Lenon; Street1 City1; 31
2; Paul McCartney; Street2 City2; 32
3; Ringo Starr; Street3 City3; 33
4; George Harrison; Address4; 34
)"};
int main()
{
// Define Variable and read complete source file
std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
// Sort the guys by name
std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
// Show output on screen
std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
return 0;
}
We have a new source format and main is unchanged. Just the extractor operator is modified. Here we are using a different iterator to get the source data.

getting selected data from a vector then storing as a variable

Ive currently stored all my data into a vector as variables. im trying to get selected data from that vector, storing then as variables so i can then do calculations, save the answers as a variable to then store back in my original vector?
The data file is in the format below;
a b c d e
1 7.3 0.8 14 74.6
2 6.5 0.1 13 3.3
3 10.8 1.4 12 75.8
4 13.2 3.5 6 32.4
my code is as below so far;
struct Weather
{
int a_data;
double b_data;
double c_data;
int d_data;
double e_data;
double ans_data;
};
int main ()
{
using std::vector;
using std::string;
using std::getline;
using std::cout;
vector<Weather> data_weather;
string line;
ifstream myfile ("weatherdata.txt");
if (myfile.is_open())
{
int count = 0;
while (getline(myfile, line))
{
if (count > 6)
{
int a, d;
double b, c, e;
std::istringstream buffer(line);
std::string sun_as_string;
if (buffer >> a >> b >> c >> d >>e_as_string)
{
if (e_as_string == "---")
{
e = 0.0;
}
else
{
std::istringstream buffer2(e_as_string);
if (!(buffer2 >> e))
{
e = 0.0;
}
}
Weather objName = {a, b, c, d, e};
data_weather.push_back(objName);
}
}
count++;
}
myfile.close();
double temp_b, temp_c, temp_ans; //declaring my new variables
for (auto it = data_weather.begin(); it != data_weather.end(); ++it)
{
std::cout << it->b_data << " " << it->c_data << std::endl;
}
}
}
else
cout << "unable to open file";
scat::pause("\nPress <ENTER> to end the program.");
return 0;
}
any help would be appreciated
Am missing something obvious or do you simply need to do this?
for (auto it = data_weather.begin(); it != data_weather.end(); ++it)
{
it->ans_data = it->b_data * it->c_data;
}
Dereferencing an iterator gives you a reference to an existing object inside the vector. You don't really need temporary variables for this.
A nicer C++11 alternative is a range based for loop:
for (Weather& w : data_weather)
{
w.ans_data = w.b_data * w.c_data;
}
Given a list of indices of lines you want to work with, you can do something like this:
Weather& w = data_weather[i]; // shortcut so you don't need to
// write data_waether[i] each time
w.ans_data = (w.b_data * w.c_data)/2;
where i is the index of the line your interested in. You might want to put this is some kind of a loop. I leave that as an exercise for you :)
I would structure this somewhat differently. I'd write the code for reading the data as an operator>> for the Weather structure:
std::istream &operator>>(std::istream &is, Weather &w) {
std::string line;
std::getline(is, line);
std::istringstream buffer(line);
buffer >> w.a >> w.b >> w.c >> w.d;
if (!buffer >> w.e) // conversion will fail for "---" or not-present
w.e = 0.0;
return is;
}
This can simplify reading the data:
std::ifstream myfile("Weather.txt");
std::string ign;
std::getline(myfile, ign); // skip that pesky first line
// Now we can initialize the vector directly from the data in the file:
std::vector<Weather> weather_data((std::istream_iterator<Weather>(myfile)),
std::istream_iterator<Weather>());
With a reasonably recent compiler, printing out the data can be simplified as well:
for (auto &w : weather_data)
std::cout << w.b_data << " " << w.c_data << "\n";
If you want to do a calculation, and put a result back into the ans field of your structure, that's fairly easy also. For example, let's assume b, c and d are temperatures, and we want ans to contain the average of the three:
for (auto &w : weather_data)
w.ans = (w.b_data + w.c_data + w.d_data)/3.0;