1

I'm trying to make a program that can store certain information in a text file. The problem I have though is that with the code I've written so far, the information stored is a bunch of weird symbols and characters. I've managed to kind of find out where it happens from but I can't seem to solve it. It seems like in my register_item function, both item number and balance get weird values for some reason. If anyone can see what mistake I've made, that would be appreciated.

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

    #define MAX 20

    struct vara
    {
        int itemnumber[20];
        char name[30];
        int balance[20];
    };

    open_file(FILE *ange_filnamn, char filnamn[], struct vara varor[], int *antal_varor)
    {
        int mainmenu = 0;
        while (mainmenu != 1 && mainmenu != 2)
        {
            printf("Do you want to open an existing file (1) or create a new one (2)?\n");
            scanf("%d", &mainmenu);
            //system("CLS");
            if(mainmenu==1)
            {
                printf("Choose filename (ex. .txt).\n");
                scanf("%s", filnamn);
                ange_filnamn=fopen(filnamn, "r+");         

                while(!feof(ange_filnamn))
                {

                    fread(&varor[*antal_varor], sizeof(struct vara), 1, ange_filnamn);
                    if(!feof(ange_filnamn))
                    {
                        *antal_varor=*antal_varor + 1;        
                    }
                }
                printf("\nNumber of items: %d \n",*antal_varor);
                fclose(ange_filnamn);              
            }

            if(mainmenu==2)
            {
                printf("What name do you want for your new file?\n");
                scanf("%s", filnamn);
                ange_filnamn=fopen(filnamn, "w+");
                printf("File is created!\n");
                *antal_varor = 0;                 
                fclose(ange_filnamn);               
            }
        }
    }

    register_item(struct vara *varor, int *antal_varor)
    {
        printf("Item number:\n");                             
        scanf("%d", varor[*antal_varor].itemnumber);    
        printf("Name:\n");
        scanf("%s", varor[*antal_varor].name);
        printf("Balance:\n");
        scanf("%d", varor[*antal_varor].balance);   
        *antal_varor+=1;
    }

    print_item(struct vara varor[], int antal_varor)
    {
        int i;
        for (i=0; i < antal_varor; i++)
        {
            printf("%d. Item number: %d Name: %s Balance: %d\n", i, varor[i].itemnumber, varor[i].name, varor[i].balance);         
        }
    }

    quit_program(char filnamn[], struct vara varor[], int *antal_varor)
    {
        FILE *fil;
        //printf("%s", filnamn);
        fil=fopen(filnamn, "w+");                                             
        fwrite(varor, sizeof(struct vara), *antal_varor, fil);
        fclose(fil);
    }

    int main(void)
    {
        FILE *ange_filnamn;
        struct vara varor[MAX];
        int mainmenu, menu, antal_varor=0;
        char filnamn[20], filen[30]; 

        open_file(ange_filnamn,filnamn, varor, &antal_varor);

        //Second menu
        while(menu!=7)
        {
            printf("\n");
            printf("1. Register new items to inventory.\n");
            printf("2. Print all items from inventory.\n");
            printf("3. Search for item.\n");
            printf("4. Change inventory.\n");
            printf("5. Sort inventory.\n");
            printf("6. Deregister item from inventory.\n");
            printf("7. Quit.\n");
            scanf("%d", &menu);

            if(menu==1)
            {
                register_item(varor, &antal_varor);
            }

            if (menu==2)
            {
               print_item(varor, antal_varor); 
            }

            if (menu==3)
            {
                printf("test");
            }

            if (menu==4)
            {
                printf("test");
            }

            if (menu==5)
            {
                printf("test");
            }

            if(menu==6)
            {
                printf("test");
            }
            if (menu==7)
            {
                quit_program(filnamn, varor, &antal_varor);
            }
        }
    }
Leonardo Alves Machado
  • 2,747
  • 10
  • 38
  • 53
Camel
  • 37
  • 7
  • 1
    What's being written to the file is the actual memory representation of the `vara` structure. In particular the integers are getting written in the binary representation (rather than text that you're probably expecting). There are issues with that which arise, but generally speaking you're okay. – Ahmed Masud Oct 04 '17 at 13:36
  • 1
    You are writing binary data, so what do you expect? You probably should use `fprintf` instead of `fwrite`. – Jabberwocky Oct 04 '17 at 13:36
  • Sorry, I'm really new to programming. What do you mean I'm writing binary data. So fwrite is used for writing binary data ? – Camel Oct 04 '17 at 13:40
  • The other comments are referring to this line, which is the source of your trouble: `fwrite(varor, sizeof(struct vara), *antal_varor, fil)`. You want to replace it with a whole series of `fprintf` calls, like `fprintf(fil, "name: %s\n", scanf("%s", varor[i].name);`. – Steve Summit Oct 04 '17 at 13:40
  • Oh okay, thanks for the clarification. – Camel Oct 04 '17 at 13:41
  • @Camel start reading a good beginner's book. In the meantime just use [`fprintf`](http://www.cplusplus.com/reference/cstdio/fprintf/) which is similar to `printf` (I hope you know that one) and which allows you "print" into a file rather than on the screen. – Jabberwocky Oct 04 '17 at 13:43
  • 1
    You write a sequence of `int`s to the file. The "weird symbols" is what `int`s look like when stored in memory. There is nothing wrong in storing the data as a binary file, you just cannot read it easily. – Bo Persson Oct 04 '17 at 13:44
  • Ok thanks for the tip. – Camel Oct 04 '17 at 13:44
  • When writing/reading binary data with `fwrite()/fread()`, best to open the file in _binary_ mode.: `fopen(filnamn, "rb+");` - add `b`. – chux - Reinstate Monica Oct 04 '17 at 13:54
  • One suggestion - use a `switch` statement to select option user entered (menu part). – Leonardo Alves Machado Oct 04 '17 at 13:59
  • @chux Well that's the big mistake I made then. Since I don't want it in binary. What's the alternative to fread then ? Since fprintf seems to be the alternative to fwrite. – Camel Oct 04 '17 at 14:06
  • @Camel To write data as _text_, for integers, use `fprintf()` and the matching specifier like `"%d"` for `int`, `"%lu"` for `unsigned long`. Add a separator like `" "`. For `double` see [Printf width specifier to maintain precision of floating-point value](https://stackoverflow.com/q/16839658/2410359). To read _text_ and convert to `int`, `double`, etc., use `fscanf()`. – chux - Reinstate Monica Oct 04 '17 at 15:21
  • @SteveSummit I tried doing it the way you described, I'm not really sure I understand how you're using that third parameter.. scanf("%s", varor[i].name) How does my quit function recognize it when it's used in another function. – Camel Oct 04 '17 at 20:14
  • @Camel I'm not quite sure what you're asking, but see the (partial) answer I'm posting now. – Steve Summit Oct 05 '17 at 03:51
  • There is absolutely no need for `#include ` in your code (or in any code for that matter). `stdio.h` provides everything you need (use `getchar()` if you need to prevent your windows console from closing instead of `getch()`. If you need `getch` like input, then use the functions in `termios.h` to place the keyboard in non-cannonical mode with `tcsetattr`. – David C. Rankin Oct 05 '17 at 07:09
  • If you want to read and write binary data, you have to open your file with "rb" and "wb" mode strings. – n. m. could be an AI Oct 05 '17 at 11:32

2 Answers2

3

You have an array of structures. The array contains antal_varor number of structures, and each structure contains members (elements) itemnumber, name, and balance.

Before we get started, a little side note: I think your structure definition has some bugs. Based on the way you're using it, I think you want

struct vara
{
    int itemnumber;
    char name[30];
    int balance;
};

But your question was about writing the file out. When you call

fwrite(varor, sizeof(struct vara), *antal_varor, fil);

you are writing out the entire array, all at once, in "binary", which is why you can't read it. If you want to write it out in a more human-readable form, you can do something like this. Here I have an explicit loop over the elements of the array, and each time through the loop, I print out all the members of that element:

int i;
for(i = 0; i < *antal_varor; i++ {
    fprintf(fil, "varor %d:\n", i);
    fprintf(fil, " itemnumber: %d\n", varor[i].itemnumber);
    fprintf(fil, " name: %s\n", varor[i].name);
    fprintf(fil, " balance: %d\n", varor[i].balance);
}

So, first try that. You should find that the output file is perfectly readable.

Now, the problem is that since you wrote the file out in this nicer, more readable format, your code that reads the data back in, that used to use

fread(&varor[*antal_varor], sizeof(struct vara), 1, ange_filnamn);

is not going to work any more. But here is the sort of code you could use to read the new-format file back in. This code reads the file line by line with fgets, figuring out what each line is, and plugging data items one by one into the varor array to rebuild it.

char line[80];
int i = 0;
*antal_varor = 0;

while(fgets(line, sizeof(line), ange_filnamn) != NULL) {
    if(strncmp(line, "varor ", 6) == 0) {
        sscanf(line, "varor %d:", &i);
        if(i >= MAX) {
            fprintf(stderr, "warning: index in file too large\n");
            i = 0;
            continue;
        }
        if(i + 1 > *antal_varor) *antal_varor = i + 1;
    } else if(strncmp(line, " itemnumber:", 12) == 0) {
        sscanf(line, " itemnumber: %d", &varor[i].itemnumber);
    } else if(strncmp(line, " name:", 6) == 0) {
        sscanf(line, " name: %s", varor[i].name);
    } else if(strncmp(line, " balance:", 9) == 0) {
        sscanf(line, " balance: %d", &varor[i].balance);
    } else {
        fprintf(stderr, "warning: unrecognized line in file\n");
    }
}

printf("\nNumber of items: %d \n",*antal_varor);
fclose(ange_filnamn); 

I haven't tested this, so there may be some little mistakes in it, but it should give you the idea.

(Also there are better ways of writing this sort of thing, but they're a little more elaborate or require more infrastructure, so I've stuck to something very simple and understandable, although it's less than robust.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • I suspect you intended `while(fgets(line, sizeof(line), ange_filnamn) != NULL)`... note `fread -> fgets`. But a very good answer otherwise. – David C. Rankin Oct 05 '17 at 07:05
  • I have a question about your tip on my structure. When I remove the square brackets, the program crashes as soon as I enter a value for item number. And when I have the square brackets, it doesn't print out the value I entered. For some reason I get other numbers like 643829. This happens to itemnumber and balance. – Camel Oct 05 '17 at 11:18
  • @Camel You have made one of the very common first mistakes with `scanf`. In `register_item()`, when you prompt for `itemnumber` and `balance`, you need to use `&` so that `scanf` receives a pointer it can filli in: `scanf("%d", &varor[*antal_varor].itemnumber);`. (You don't need the `&` with `name`, because `name` is an array.) This is confusing at first, I know. – Steve Summit Oct 05 '17 at 11:22
  • @SteveSummit Oh my god. Ofcourse. Sorry for the stupid mistake. The char array confused me. – Camel Oct 05 '17 at 11:31
0

The commenters on your question have it right.
- What is displayed with printf (and written to a file with fprintf) is ASCII representation of the numbers.
- What is stored in memory (and written with fwrite) is the actual "binary" value.
In C, an int variable always takes up the same number of bytes in memory, regardless of the value stored. That is why your sizeof() works consistently. Reading and writing can be consistently done.

(Note that not all C implementations use the same size int though. It is 4 bytes in the x86 Linux I'm using right now).

When displaying the ASCII representation, the number of ASCII digit characters required depends on the value. So, if reading values in that have been stored this way, you have to 'parse' the text and build up the integer value from the digits read, and in general will be a variable number of digits. (This is what scanf does.) Hence, reading and parsing ASCII could be considered more complicated than just reading in a value stored as binary int that is always the same size.

IMPORTANT: If you are going to read the file as binary, you should open it with the "b" attribute like this:

ange_filnamn=fopen(filnamn, "r+b");

Similary, to open for binary write:

fil=fopen(filnamn, "w+b");