0

I'm new one in C++ and have some misunderstandings about file read/write...

I have a .txt data file that contains information about the streets i.e. street, home number and number of flats

I have a task to read data from a file, add new one, delete unwanted and make some calculations about the number of flats on certain street

For data storage during the program work I made a vector of struct:

struct homeAdressBook {
    std::string street;
    char homeNumber[255];
    int flats;
};

std::vector<homeAdressBook> HAB;

Program code is below:

std::fstream document;
    document.open("name.txt");
    if (!document) {
        std::ofstream document;
        document.open("name.txt");
    }
    if (!document)
        std::cout << "File open error" << std::endl;
    else {
//Here (while loop) I want to read data from a file and add to my vector of struct for further usage, it partly works somehow but this point is unclear for me
        while (!document.eof()) {
            HAB.push_back(homeAdressBook());
            std::getline(document, HAB[i].street);
            document >> HAB[i].street;
            document >> HAB[i].homeNumber;
            document >> HAB[i].flats;
            i++;
        }
        document.close();
        do {
            switch (menu()) {
            case 0: {
                document.close();
                exit = false;
                break;
            }
//Here (case 1) I add new data and write it in file, here I have no problems at the moment
            case 1: {
                document.open("name.txt", std::ios::app);
                HAB.push_back(homeAdressBook());
                std::cin.ignore();
                std::cout << "Enter the address: ";
                std::getline(std::cin, HAB[i].street);
                std::cout << "Enter home number: ";
                std::cin.getline(HAB[i].homeNumber, 255);
                std::cout << "Enter number of flats: ";
                std::cin >> HAB[i].flats;
                document << HAB[i].street << HAB[i].homeNumber << HAB[i].flats << std::endl;
                document.close();
                i++;
                break;
            }
//Here (case 2) I want to read data from a file, it reads data except first line of data
            case 2: {
                int total = i;
                std::cout << std::setw(20) << "Street" << std::setw(20) << "Home Number" << std::setw(20) << "Number of flats" << std::endl;
                document.open("name.txt", std::ios::in);
                for (int j = 0; j < total; j++) {
                    std::cout << std::setw(20) << HAB[j].street << std::setw(20) << HAB[j].homeNumber << std::setw(20) << HAB[j].flats << std::endl;
                }
                document.close();
                break;
            }
//Here (case 3) data should be deleted from a file if necessary
            case 3: {
                document.open("name.txt", std::ios::out | std::ios::in);
                int strDelete = 0;
                std::cout << "0. Exit to main menu" << std::endl;
                for (i = 0; i < HAB.size(); i++) {
                    std::cout << i + 1 << ". " << HAB[i].street << std::endl;
                }
                std::cout << "\nType number of street to delete: ";
                std::cin >> strDelete;
                if (strDelete)
                    i--;
                else break;
                HAB.erase(HAB.begin() + (strDelete - 1));
                document.close();
                break;
            }
            }
        } while (exit);
    }
    system("pause");
    return 0;
}

Data in .txt file is written like this:

aaa street 10 50
aaa street 10 45
bbb street 1 10
ccc street 1 12
ccc street 1/10 25

Each line in .txt file belongs to [i]-s vector of struct elements I cannot understand how correctly add data from a .txt file to my vector of structs during the program work (name of the street can hold up to 5 or 6 words and how program can separate whether it is the name of the street or it is home number or number of flats)

Alhin
  • 11
  • 1
  • 2
  • 3
    Please take some time to read [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) – Some programmer dude May 05 '18 at 11:36
  • Thank you for your answer! It was helpful and I already tried to do like this: `unsigned int i = 0; while (document >> HAB[i].street >> HAB[i].homeNumber >> HAB[i].flats) { HAB.push_back(homeAdressBook()); i++;` Program gives an error: Vector subscrip out of range But if make another string object and keep there data during reading, than I need to work with that string but not with vector struct – Alhin May 05 '18 at 12:00
  • 1
    Read into temporary variables and push back into the vector in the loop? – Some programmer dude May 05 '18 at 12:04
  • I just cannot understand the logic of how all this things happen I have a file with data, with 'document >> HAB[i].street' I give the way where information will be stored, then I push back vector to go to another vector of struct element to store another sort of data there... where I'm wrong? – Alhin May 05 '18 at 12:39
  • But when you do it like that, the very first iteration of the loop `HAB` will be *empty* and all indexing will be *out of bounds* and lead to *undefined behavior*. You could solve it by pushing back first outside the loop, but then at the end you will have one unused element in the vector. – Some programmer dude May 05 '18 at 12:42

1 Answers1

1

I recommend that you overload operator>> for your structure:

struct homeAdressBook
{
    std::string column1;
    std::string street;
    std::string homeAddress;
    int flats;
    friend std::istream& operator>>(std::istream& input, homeAddressBook& address);
};

std::istream& operator>>(std::istream& input, homeAddressBook& address)
{
  input >> address.column1;
  input >> address.street;
  input >> address.homeAddress;
  input >> address.flats;
  return input;
}

The above will allow you to change your input process to:

std::vector<homeAddressBook> database;
homeAddressBook record;
//...
std::ifstream data_file("addresses.txt");
while (data_file >> record)
{
  database.push_back(record);
}

Notes

  1. Your post describes 3 columns in the data, but your data source shows 4.
  2. You open your document as an output stream (ostream) and you use getline on the output stream.
  3. Your structure name has "book" which implies a collection of items. I recommend using a name such as "Home_Address" to indicate a single item. Use std::vector for the book.

Edit 1:
Prefixed members in operator>> with "address.".

Thomas Matthews
  • 52,985
  • 12
  • 85
  • 144
  • Thank you for your answer Thomas! But where you declare operator overload i.e. 'input >> column1; input >> street ...' When I use it in my code IDE curses at me - column1, street, flat, homeNumber is undefined.. If I add like this: 'database[i].column1' it's not underlined with red line, but then I believe I need to declare 'int i = 0' and add 'for()' loop to increment 'i'... What about 'getline' in 'ostream' (your note #2) it gets the line from user and append it to my vector, is it wrong to do so? – Alhin May 13 '18 at 06:26
  • You can find the declaration in the `struct`, the line above the terminating `}`. – Thomas Matthews May 13 '18 at 16:24
  • I corrected the implementation of `operator>>`, see my latest edit. BTW, you can find examples of this pattern by searching the internet for "c++ read file struct space separated". – Thomas Matthews May 13 '18 at 16:27
  • I'm not using `std::getline` because it reads the entire record into a string. Remember that `std::getline` is an **input** function and requires an **`std::istream`** derived object (e.g. the 'i' prefix is for input, such as `ifstream`). The `std::getline` doesn't work with output streams (e.g. `ofstream`). The `operator>>` for string will read characters until a whitespace is encountered. – Thomas Matthews May 13 '18 at 16:28