简体   繁体   中英

What is the best way to store data from text file into C++ in order to Edit

I am building a Contact Management System in which I have console C++ application and I want to store contacts in a address text file like this.

1
John Doe
134 main st.
south bend, IN. 38744
574-444-5554

I want to be able to store multiple contacts to the file, then read the file into memory, but not sure if I should store into an array, vector or some other data structure.

I need /want to be able to ADD, Edit, Search by Last Name, ID (1, etc..)

Currently, when I put data in a text file, I can open it and read it out, but it's not stored in any organized data structure.

Example of how I'm reading the file:

    string myText;
    // Read from the text file
    ifstream MyReadFile("address.txt");
    // Use a while loop together with the getline() function to read the file line by line
    while (getline(MyReadFile, myText)) {
        // Output the text from the file
        cout << myText << "\n";
    }
    // Close the file
    MyReadFile.close();

C++ is an object oriented language. It helps you to abstract your real world problems into objects, consisting of data and methods, operating on this data.

So, one of the first idea could then be to translate a "Contact" into an object. And such an object (or class) would have properties or data members that belong together and then make up a "Contact"

Example: You could define a class contact like this:

#include <string>

struct Contact {
    int ID{};
    std::string name{};
    std::string street{};
    std::string city{};
    std::string phone{};
};

With that all attributes that make up a contact, are together in on object and can be easliy handled.

And if you need an instance of such a Contact, you could simply write Contact c and you can easily access all data with eg c.name .

This makes life already very simple.

Next. The methods. What can you or do you want to do with ONE contact. The only requirement that you have at the moment is, to read the data from or write them to a stream. C++ will help you also here. You know already about the iostream facilities and that you can for example use the inserter operator << to write to any stream. It does not matter if the stream is a std::cout or any file stream or even std::ostringstream . It will always work.

That is nice, but how to add such functionality to your "Contact" object? This can be done be overwriting operators << and >> for your object. The class would now look like this:

#include <iostream>
#include <string>
#include <iomanip>

struct Contact {
    int ID{};
    std::string name{};
    std::string street{};
    std::string city{};
    std::string phone{};

    friend std::istream& operator >> (std::istream& is, Contact& c) {
        is >> c.ID;
        std::getline(is >> std::ws, c.name);
        std::getline(is, c.street);
        std::getline(is, c.city);
        std::getline(is, c.phone);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Contact& c) {
        return os << c.ID << '\n' << c.name << '\n' << c.street << '\n'
            << c.city << '\n' << c.phone << '\n';
    }
};

Now you need to think about more abstractions. You want to have not only one instance of a contact, but many of them. And you want to work with this data, so for example, searching, adding, sorting or whatever.

This can of course not be done with the above object as is. We need to store one or many objects of it.

And for this the absolute standard approach is to use a std::vector . The std::vector can hold 0..many other objects and is ideally suited for your requirements. You can write simply std::vector<Contact> contacts; and you immediately can have many instances of "Contacts".

But in C++ you would go one step ahead. Because you want to operate on these many "contacts", you would encapsulyte also this in a new, different object. Let us call this "Database". And here you would add all functions that work with a "Contact". So, the data is a std::vector<Contact> and the mehods will operate on this data. Let us start with input and output.

Next source code:

#include <iostream>
#include <vector>
#include <string>
#include <iomanip>

struct Contact {
    int ID{};
    std::string name{};
    std::string street{};
    std::string city{};
    std::string phone{};

    friend std::istream& operator >> (std::istream& is, Contact& c) {
        is >> c.ID;
        std::getline(is >> std::ws, c.name);
        std::getline(is, c.street);
        std::getline(is, c.city);
        std::getline(is, c.phone);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Contact& c) {
        return os << c.ID << '\n' << c.name << '\n' << c.street << '\n'
            << c.city << '\n' << c.phone << '\n';
    }
};

struct Database {

    std::vector<Contact> data{};

    friend std::istream& operator >> (std::istream& is, Database& d) {
        d.data.clear();
        Contact tmp;
        while (is >> tmp)
            d.data.push_back(tmp);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Database& d) {
        for (const Contact& c : d.data)
            os << c;
        return os;
    }
};

This is now already very powerful. If you have a Database d , you can output the complete database to the console with on simple statement: std::cout << d; . This will show all data on std::cout . And all functionionality is encapsulated in the classes. That is a big advantage. Imagine that you make a modfication in the "Contact" class. The "outer" world will not be affected and continue to work without any modification.

Let us go now to a full example. For this I would like to remind you that our class is now working together with any type of stream. So, I will put some test data in a std::istringstream instead of a file. Let us have a look:

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <iomanip>

struct Contact {
    int ID{};
    std::string name{};
    std::string street{};
    std::string city{};
    std::string phone{};

    friend std::istream& operator >> (std::istream& is, Contact& c) {
        is >> c.ID;
        std::getline(is >> std::ws, c.name);
        std::getline(is, c.street);
        std::getline(is, c.city);
        std::getline(is, c.phone);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Contact& c) {
        return os << c.ID << '\n' << c.name << '\n' << c.street << '\n'
            << c.city << '\n' << c.phone << '\n';
    }
};

struct Database {

    std::vector<Contact> data{};

    friend std::istream& operator >> (std::istream& is, Database& d) {
        d.data.clear();
        Contact tmp;
        while (is >> tmp)
            d.data.push_back(tmp);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Database& d) {
        for (const Contact& c : d.data)
            os << c;
        return os;
    }
};

std::istringstream in{ R"(1
John Doe
134 main st.
south bend, IN. 38744
574-444-5554
2
Jane Doe
2 second  st.
north , IN. 5678
543-123-456
3
x y
z  st.
city
phone)" };

int main() {
    Database d{};

    in >> d;
    std::cout << d;
}

Of course you now need to add many more functions to your "Database" class. Whatever you need.

And if you want to restructure the "Contact" class. Also no problem. It will continue to work.

I hope that this example gave you and idea on hiw things could be done.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM