0

i am creating this program that stores some items into the (.dat) file.. the program seems to run smoothly but when i try to read the data from the file i see special character on my screen and none of the data that i was trying to save, was saved properly, note that when i run the similar code in main() it works and it actually shows me the correct output.. please help me on this i really don't know what to do here

void viewFile() {
    Product item;
    ifstream file("products.dat", ios::binary);
    if (!file.read(reinterpret_cast<char*>(&item), sizeof(Product))) {
        cout <<"failed to read";
        system("exit");
    }
    item.viewProduct();
    while (!file.eof()) {
        item.viewProduct();
        cout <<endl <<endl;
        file.read((char *)(&item), sizeof(Product));
    }
    file.close();
}
void addProductToInventory() {
    string name;
    int quantity;
    int pricePerItem;
    ofstream obj("products.dat", ios::out | ios::binary);

    Product item;
    int numberOfProducts;
    cout <<"enter the number of products you want to add in this inventory: ";
    cin >> numberOfProducts;

    for (int counter = 1; counter <= numberOfProducts; counter++) {
        Product product;        
        cout <<"enter the name of the object: ";
        cin >> name;
        cout <<"enter the quantity of the product: ";
        cin >> quantity;
        cout <<"enter the price of this product (per item): ";
        cin >> pricePerItem;

        product.setName(name);
        product.setQuantity(quantity);
        product.setPricePerItem(pricePerItem);

        if (!obj.write(reinterpret_cast<char*>(&product), sizeof(Product))) {
            cout <<"failed writing\n";
            system("exit");
        }
        cout <<"item added\n\n";
    }
    obj.flush();
    obj.close();
}

this is my code in main() which WORKS its exactly the same code.. i guess

ofstream file ("products.dat", ios::out | ios::binary | ios::trunc);
    Product p1("hammer", 12, 3);
    Product p2("screw driver", 43, 1);
    if (!file.write(reinterpret_cast<char*>(&p1), sizeof(Product))) {
        cout <<"failed to write";
        system("exit");
    }
    file.write(reinterpret_cast<char*>(&p2), sizeof(Product));
    file.close();
    ifstream file2("products.dat", ios::out | ios::binary);
    if (!file2.read(reinterpret_cast<char*>(&p1), sizeof(Product))) {
        cout <<"failed to read";
        system("exit");
    }
    while (!file2.eof()) {
        p1.viewProduct();
        cout <<endl <<endl;
        file2.read((char *)(&p1), sizeof(Product));
    }
    file2.close();
}

P.S i am really sorry if this comes out to be a messy question.. I have been debugging this for hours and now i can't even think straight.

8
  • 2
    You can't write and read non-trivial objects like that. Read about serialization. Commented Apr 14, 2020 at 17:11
  • You also need to read Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? Commented Apr 14, 2020 at 17:11
  • None of this code will work properly. Who gave the assignment to you to write these types of objects to binary files? Commented Apr 14, 2020 at 17:13
  • 1
    The reason why it will not work is self-evident if you consider what sizeof(Product) is. That value will never change, since it is a compile-time constant. Now if Product contains members that are std::string, then sizeof(Product), regardless of how long those strings are, will remain the same. So how will the file magically save, I don't know, 1000 characters if sizeof(Product) is, say 40 bytes? You want to save the data that Product represents, not the raw bytes of the Product object. That's what serialization is all about. Commented Apr 14, 2020 at 17:18
  • 1
    Yes, use character arrays. But my example is in theory why your solution could never work. Your Product must have members that are plain-old-data types and not contain pointers for any of this to work properly. Commented Apr 14, 2020 at 17:25

1 Answer 1

1

Lets start by thinking about how pointers work.

char* a = new char[2];
a[0] = 'a';
a[1] = 'b';
char* b = a;
std::cout << a[0] << a[1]; //prints ab
std::cout << b[0] << b[1]; //prints ab
a[1] = 'c';
std::cout << b[0] << b[1]; //prints ac
delete[] a;
std::cout << b[0] << b[1]; //prints random garbage because the memory that b points at is deleted

know lets start writing these pointers to files

char* a = new char[2];
a[0] = 'a';
a[1] = 'b';
ofstream outFile("products.dat", ios::binary | ios::trunc);
outFile.write(reinterpret_cast<char*>(&a), sizeof(a));
outFile.close();
{
    ifstream inFile("products.dat", ios::binary);
    char* b;
    inFile.read(reinterpret_cast<char*>(&b), sizeof(b));
    std::cout << b[0] << b[1]; //prints ab
    a[1] = 'c';
    std::cout << b[0] << b[1]; //prints ac
}
delete[] a;
{
    ifstream inFile("products.dat", ios::binary);
    char* b;
    inFile.read(reinterpret_cast<char*>(&b), sizeof(b));
    std::cout << b[0] << b[1];//prints random garbage because the memory that b points at is deleted
}

std::string contains pointers to dynamically allocated memory.

You should do something like this.

void viewFile() {
    ifstream file("products.dat", ios::binary);
    while (!file.eof()) {
        int nameLength = 0;
        if (!file.read(reinterpret_cast<char*>(&nameLength), sizeof(nameLength))) {
            cout <<"failed to read";
            system("exit");
        }
        std::string name{ nameLength, '?' };
        if (!file.read(name.data(), sizeof(char) * nameLength) {
            cout <<"failed to read";
            system("exit");
        }
        int quantity;
        if (!file.read(reinterpret_cast<char*>(&quantity), sizeof(quantity)) {
            cout <<"failed to read";
            system("exit");
        }
        int pricePerItem;
        if (!file.read(reinterpret_cast<char*>(&pricePerItem), sizeof(pricePerItem)) {
            cout <<"failed to read";
            system("exit");
        }
        Product item{ std::move(name),quantity, pricePerItem };
        item.viewProduct();
    }
    file.close();
}
void addProductToInventory() {
    string name;
    int quantity;
    int pricePerItem;
    ofstream obj("products.dat", ios::out | ios::binary);

    int numberOfProducts;
    cout <<"enter the number of products you want to add in this inventory: ";
    cin >> numberOfProducts;

    for (int counter = 1; counter <= numberOfProducts; counter++) {   
        cout <<"enter the name of the object: ";
        cin >> name;
        cout <<"enter the quantity of the product: ";
        cin >> quantity;
        cout <<"enter the price of this product (per item): ";
        cin >> pricePerItem;

        int nameLength = name.size();
        if (!obj.write(reinterpret_cast<char*>(&nameLength), sizeof(nameLength))) {
            cout <<"failed writing\n";
            system("exit");
        }
        if (!obj.write(name.data(), sizeof(char) * nameLength) {
            cout <<"failed writing\n";
            system("exit");
        }
        if (!obj.write(reinterpret_cast<char*>(&quantity), sizeof(quantity))) {
            cout <<"failed writing\n";
            system("exit");
        }
        if (!obj.write(reinterpret_cast<char*>(&pricePerItem), sizeof(pricePerItem))) {
            cout <<"failed writing\n";
            system("exit");
        }
        cout <<"item added\n\n";
    }
    obj.flush();
    obj.close();
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.