0

I have recently been trying to write a file writing program that saves inventory statistics of part number, the quantity and a price for the part. While writing to my binary file my scanf saves my prices, but when I read them in my next program, it comes out with a slew of meaningless numbers, that are not what I input. Compiler with write program:(* * is user input)

This program stores a business inventory.
Please enter item data (part number, quantity, price): *2, 3, 1.6*
Please enter item data (part number, quantity, price): *3, 1, 5.3*
Please enter item data (part number, quantity, price): *0*
Thank you. Inventory stored in file inventory.txt

Write Program Code

   #include <stdio.h>
#include <stdlib.h>

int main(int argc, int argv[])
{
int pnum=1, quantity;
float price;
FILE *fp1;

fp1 = fopen("inventory.txt", "wb+");
if(fp1 == NULL)
{
    printf("Can't open!\n");
    exit(EXIT_FAILURE);
}

printf("This program stores a business inventory.\n");
while(pnum != 0)
{
printf("Please enter item data (part number, quantity, price): ");
scanf("%d, %d, %f", &pnum, &quantity, &price);
printf("%d, %d, %f", pnum, quantity, price);
fwrite(&pnum, sizeof(int), 1, fp1);// Is there a way to combine these 3 fwrites into 1?
fwrite(&quantity, sizeof(int), 1, fp1);
fwrite(&price, sizeof(float), 1, fp1);
}
printf("Thank you. Inventory stored in file inventory.txt");
fclose(fp1);
return 0;


}

Compiler with read program (* * is user input)

Below are the items in your inventory.
Part#    Quantity        Item Price
2         3                1070386381?
3         1                1084856730?
0?        1                1084856730?

Read Program Code

#include <stdio.h>
#include <stdlib.h>

int main()
{
int pnum, quantity;
float price;
FILE *fp1 = fopen("inventory.txt", "rb");
if(fp1 == NULL)
{
    printf("Can't open!");
    exit(EXIT_FAILURE);
}
printf("Below are the items in your inventory.\n");

printf("Part#\tQuantity\t Item Price\n");
while (fread(&pnum, sizeof(int), 1, fp1) == 1)//Is there a way to combine these 3 freads into 1 line of code?
{
    printf("%5d\t", pnum);
}
while (fread(&quantity, sizeof(int), 1, fp1) == 1)
{
    printf("%8d\t", quantity);
}
while (fread(&price, sizeof(float), 1, fp1) == 1)
{
    printf("$");
    printf("%9.2f\n", price);

}
fclose(fp1);
return 0;

}

As you may of seen, scanf is being scanf and must have to do with my float, but I haven't been able to figure out how to fix it, because without scanf nothing gets saved to my inventory.txt file (I didn't include the .txt file because it's binary), and for some reason when I type in 0 to break the loop, it saves the 0 in the file. If any other info is needed I can supply it, but I think I've supplied everything. Thank you for any help, and happy coding :)

Tcranmer
  • 29
  • 1
  • 10
  • 2
    I don't see how the "read program" generates that output. The first `while` loop will consume the entire file, and print all the numbers on a single line. – user3386109 May 08 '18 at 23:06
  • I added /t to my read printfs, as because the quantity and price combined (no space) when being printed so it looks like 31070386381. You mean my first 'while' fread for pnum? – Tcranmer May 08 '18 at 23:12
  • That identifies the problem, but fixing it on the other hand, well I don't quite get how to fix it, i'm still a bit green with file IO – Tcranmer May 08 '18 at 23:18
  • 2
    You're writing things in this order: pnum, qty, price, pnum, qty, price, pnum, qty, price. And then you're reading them in this order: pnum, pnum, pnum, pnum, pnum, pnum, pnum, pnum, pnum. You need to read them in the same order you write them. – user253751 May 08 '18 at 23:21
  • 1
    The "read program" needs a single `while` loop that contains the three `fread`. – user3386109 May 08 '18 at 23:23
  • Implementing my 3 whiles into 1 big while with a lot of &&s fixed my problem, and is giving me desired results. Thank you everyone for the assistance. – Tcranmer May 08 '18 at 23:47

2 Answers2

1
  • The zero is inserted into the array because your while loop only breaks after the fwrite calls. You can use break I believe to exit right after the scan.
  • To combine the writes and reads you can use a struct, but notice that you should probably serialize and deserialize from/into exactly the same struct because of padding.
  • I suspect that your freads into float yield strange results because of wrong write/read order or padding. Try reading and writing into a struct and see if that solves your issue. If that doesn't work try printing the hexadcimal value of your float with %x. Then compare to the expected result.

Consider the following code:

#include <stdio.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close


typedef struct
{
    float price;
    int pnum;
    int quantity;
} shoppingItem;


void writeToFile(FILE *fp) {    
    shoppingItem input1 = {1.1,2,3};
    shoppingItem input2 = {3.1412,42,666};
    fwrite(&input1, sizeof(shoppingItem), 1, fp);
    fwrite(&input2, sizeof(shoppingItem), 1, fp);
    printf("%f %d %d\n", input1.price, input1.pnum, input1.quantity);
    printf("%f %d %d\n", input2.price, input2.pnum, input2.quantity);
}

void readFromFile(FILE *fp) {
    shoppingItem output1 = {0.0,0,0};
    shoppingItem output2 = {0.0,0,0};
    fread(&output1, sizeof(shoppingItem), 1, fp);
    fread(&output2, sizeof(shoppingItem), 1, fp);
    printf("%f %d %d\n", output1.price, output1.pnum, output1.quantity);
    printf("%f %d %d\n", output2.price, output2.pnum, output2.quantity);
}

int main()
{
    FILE *fp = fopen("inventory.txt", "wb+");
    if(fp == NULL)
    {
        printf("Can't open!\n");
        return 1;
    }

    writeToFile(fp);
    rewind(fp);
    readFromFile(fp);

    if (fclose(fp) == 0) {
        printf("Done\n");
        return 0;
    } else {
        printf("Error on closing file\n");
        return 1;
    }
}
Alechko
  • 1,406
  • 1
  • 13
  • 27
  • 1
    Thank you for your input; when inplementing it, it worked perfectly, I was able to also figure it out by implementing some comment ideas in a non struct version, that also worked. Turns out, putting all the freads into 1 while loop using &&s fixed my stated program. Thank you for your help! – Tcranmer May 08 '18 at 23:46
  • 1
    Writing a raw struct to a file and reading it back is not a good idea and generally should be avoided. Playing with memory around structures is like playing with a lighter on a gas station. – itachi May 08 '18 at 23:57
  • This question demonstrates how not using structs can cause incorrect write/read order when mixing types. In which situation for example using a struct would be a worse approach than individual writes of multiple different types? – Alechko May 09 '18 at 01:11
  • I would agree that it is altogether not safe to play with memory in C like that (due to lack of built abstractions), since serializing and deserializing on different system is likely to yield different results. – Alechko May 09 '18 at 01:19
  • @AlexGordon if the write/read order is preserved, nothing can go wrong, because files are always written the same way - from beginning to end. One should be cautious when implementing something like that, but it is doable. As you can see in my answer, the readings are good, but for some reason they are not converted from IEEE-754 to a decimal value. Structures should not be expected to be aligned in memory the same way they are in the code, even when you use dozens of magic compiler flags. – itachi May 09 '18 at 09:47
  • @itachi That's the entire point! **if** the write/read order is preserved. With structs you don't have this big if since we would always be serializing the same type. – Alechko May 09 '18 at 09:48
  • It's true that in the original question the order was not the reason of failure. But using a struct would have removed some noise and wouldn't harm. – Alechko May 09 '18 at 09:50
1

The strange values you get in you "reading program" are completely valid but... They are encoded as IEEE-754 floating point value. If you encode 5.3 as IEEE-754, you'll get 0x40a9999a which is equal to 1084856730, so your readings are good and something's bad with outputting it.

Change your printf format from %9.2f to just %f. If that doesn't help, try to force casting of price: fread((void*)(&price), sizeof(float), 1, fp1).

For the future - avoid storing data in a format like that. If you would try to read that file on a machine with a different endianess, the result will be completely different.

itachi
  • 3,389
  • 2
  • 24
  • 29
  • Seems like indeed on the OP's machine freading float values from file changes the encoding. It's either an endianness issue, or `fread` and `fwrite` of floats are using different encodings for float values (unlikely as they operation that work with binary data). – Alechko May 09 '18 at 10:04