0

I'm new to C++ and stackoverflow so forgive me any mistakes in my post ;). I need to create a code, which allows me to fill new objects with data from std::cin and export these objects to binary file later. Also, I need to import objects exported to file at some point. Objects represent users with standard user information like username, ID, lvl etc.

#include <vector>
#include <string>
#include <iostream>
class User {

    std::string username;
    unsigned int ID, lvl;
public:

    User(std::string un, int uID, int ulvl) {

        username = un;
        ID = uID;
        lvl = ulvl;
    }
};
int main() {
    std::string u_name;
    int u_ID,u_lvl;
    bool finish = false;
    char choice;
    std::vector<User> test_user_vec;
    do {

        std::cout << "Enter username: ";
        std::cin >> u_name;
        std::cout << "Enter ID: ";
        std::cin >> u_ID;
        std::cout << "Enter lvl: ";
        std::cin >> u_lvl;
        test_user_vec.push_back(User(u_name, u_ID, u_lvl));
        std::cout << "Do you want to add another user? (y/n)?";
        choice = getch();
        if (choice == 'y') finish = true;
    } while (!finish);

return 0;
}

I assume that test_user_vec stores every object I created while my program is running. My problem occurs when I want to export that vector to file. The purpose of this action is to store objects' data even after my program terminates and import all the data when I run my program again.

I was trying to solve this problem on my own, nothing really came to my mind. While I was looking for some info i found something like this:

#include <fstream>
#include <vector>
#include <string>

int main()
{
    std::vector<std::string> v{ "one", "two", "three" };
    std::ofstream outFile("my_file.txt");
    // the important part
    for (const auto &e : v) outFile << e << "\n";
}

I've tested it with <string> and <int> vectors and my variables. It's good until I try to export <object>vector. Also i found another solution and tried to do something with it on another test code:

class Test {
public:
    int number;
    float number2;
};
int main(){
Test test1;
    test1.number = 122;
    test1.number2=12;
    
    
    std::fstream testfile("test1.bin", std::ios::out | std::ios::binary);
    testfile.write((char*)&test1, sizeof(test1));
        testfile.close();
        //ater writing an object with variables i commented this section 
    //then uncommented this section and run the program again
    std::fstream testfile2("test1.bin", std::ios::in);
    testfile2.read((char*)&test1, sizeof(test1));
std::cout << test1.number;
testfile2.close();
return 0;
}

Again, it works, i can read test1.number until I want to use vector of objects, not a single object. With vector of objects my cout printed some random values like 11314123e-03. I was trying to somehow combine these 2 solutions, but nothing worked out. I would like to have a binary file, because i heard it's faster and has any data protection (i can't just open it in notepad and read the data) I'm new to c++, there is a great chance of me trying to do it reeeeeealy inefficient way, so pls help :D.

Gh0stB0y
  • 1
  • 1
  • What did you do for a vector? If the `Test` class contained a std::vector or even a std::string you can longer `testfile.write((char*)&test1, sizeof(test1));` this will not work because vectors, and other containers of the standard library store their items outside the object using some pointer. – drescherjm Nov 17 '22 at 23:16
  • 1
    Does this answer your question? [Serialize and deserialize vector in binary](https://stackoverflow.com/questions/3438132/serialize-and-deserialize-vector-in-binary) – drescherjm Nov 17 '22 at 23:50

1 Answers1

1

Data member getter functions can be added to the User class and used in fstream output operations. This should provide the general idea:

std::string userName;

for (const auto &u : v)
{
   outFile.write(u.GetID(), sizeof(int));
   outFile.write(u.GetLvl(), sizeof(int));

   userName = u.GetName();
   outFile.write(username.length(), sizeof(size_t));
   outFile.write(userName.data(), username.length());
}

For userName, the length is written to precede the userName string data in the file so that the file can be parsed when read. The binary encoding/convention is designer's decision as there are several options. Another option would be to encode the entire object as a null-terminated string, although this would generally be less size efficient except for the userName string itself.

Note: test_user_vec.push_back(User(u_name, u_ID, u_lvl)); is creating temporary User objects on the stack. As @drescherjm and @RaymondChen pointed out, that is OK, but this is a better alternative: test_user_vec.emplace_back(...);

Coder
  • 133
  • 7
  • 2
    There's nothing technically wrong with passing a temporary. It will be moved into the vector. Emplacement is a more efficient, but moving is not *wrong*. – Raymond Chen Nov 18 '22 at 00:27
  • @drescherjm and RaymondChen, thank you for true professionalism and courtesy. – Coder Nov 18 '22 at 20:02