-1

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;
}
donjuedo
  • 2,395
  • 14
  • 26
  • 1
    What is the value of `i` when you read the first line of the file? When you read the last line of the file? When you read the last/2 line? – JohnFilleau Feb 24 '20 at 18:59
  • Have you ever heard of the Golden Rule Of Computer Programming, "your computer always does exactly what you tell it to do, instead of what you want it to do"? Can you explain, step by step, what exactly does that `while` tell your computer to do? And then, exactly, what your `for` loop tells your computer to do? – Sam Varshavchik Feb 24 '20 at 19:01
  • i is initialize as 0, and the content is a total of 30 lines of strings. – nung khual Feb 24 '20 at 19:01
  • 1
    `while (!patientss.eof()) {` avoid this pattern [https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) – drescherjm Feb 24 '20 at 19:02
  • The question isn't what the content is, or how many strings it is. The question is what exactly does your `while` loop and your `for` loop tell your computer to do? – Sam Varshavchik Feb 24 '20 at 19:02
  • 2
    What is the value of `i` when you read the first line of the file? When you read the last line of the file? When you read the last/2 line? – JohnFilleau Feb 24 '20 at 19:04
  • `i` never changes in the first loop so whatever data you read from the `Patient.txt` it populates only one index of your array total. It's also unclear what value `i` has since that is not in the code presented. – drescherjm Feb 24 '20 at 19:05
  • Then if `i` was fixed to increment so the first file reading would work, what value would it have when we entered this loop; `for (i; i < 30; i++) {` – drescherjm Feb 24 '20 at 19:11
  • 1
    Unrelated: once you get the rest working, `file >> arr[i].name >> arr[i].DOB >> arr[i].address >> arr[i].Dr_name >> arr[i].V_date;` will read words from a file, so you get garbage like the patient's last name being read into `DOB`. You'll need more smarts here. Similarly `files << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date;` will output one long blob of text with no way for a program to tell one field from another. You will want to output a delimiter. – user4581301 Feb 24 '20 at 19:12

2 Answers2

2

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;
        }
    }
}
Ted Lyngmo
  • 37,764
  • 5
  • 23
  • 50
2

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).

Thomas Matthews
  • 52,985
  • 12
  • 85
  • 144