Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading and writing binary files in C

These are 2 separate applications.

  • In the first one, I tried to store employee details like name, age and salary in the binary file named emp.bin.
  • In the second application, I tried to view the contents of the file but in place of the name, only the first character appears.

I tried printing each character separately, and it turns out that there's 3 null characters '\n' after each letter in the name that is why it is not printing after the first character.

"Write" application code:

//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>

int main()
{
    FILE *fp;
    char another = 'Y';
    struct emp
    {
        char name[40];
        int age;
        float bs;
    };
    struct emp e;
    fp = fopen("emp.bin", "wb");
    if (fp == NULL)
    {
        puts("Cannot open the file.");
        return 1;
    }
    while(another == 'Y')
    {
        printf("Enter the employee name, age and salary: ");
        scanf("%S %d %f", e.name, &e.age, &e.bs);
        while(getchar() != '\n');
        fwrite(&e, sizeof(e), 1, fp);
        printf("Add another record? (Y/N)");

        another = getchar();
    }
    fclose(fp);
    return 0;
}

"Read" application code:

//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>

int main()
{
    FILE *fp;
    struct emp
    {
        char name[40];
        int age;
        float bs;
    } e;
    fp = fopen("emp.bin", "rb");
    if (fp == NULL)
    {
        puts("Cannot open the file.");
        return 1;
    }
    while (fread(&e, sizeof(e), 1, fp) == 1)
    {
        printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
    }
    fclose(fp);
}

Here's the input and output:

enter image description hereenter image description here

How can I correct this code to make it print the whole name?

like image 671
0x5961736972 Avatar asked May 22 '20 14:05

0x5961736972


2 Answers

The problem is in the "writer" application, even before the actual write is performed.

When you get data from the user

scanf("%S %d %f", e.name, &e.age, &e.bs);

you use format %S (capital letter "S". Format specifiers are case sensitive!). As we can read in the printf man page

S
(Not in C99, but in SUSv2.) Synonym for ls. Don't use.

this leads us to %ls format specifier that is described in the following way

s
[...] If an l modifier is present: The const wchar_t * argument is expected to be a pointer to an array of wide characters. Wide characters from the array are converted to multibyte characters

Talking about Windows source we have:

S
Opposite-size character string, up to first white-space character (space, tab or newline). [...]
When used with scanf functions, signifies wide-character array; when used with wscanf functions, signifies single-byte-character array [...]

So, basically, you are reading characters from stdin and converting them to wide chars. In this case every character takes sizeof(wchar_t). Probably in your system this size is 4.


What you need is simply %s format specifier. And since your name array has size 40, I suggest using

scanf("%39s", e.name );

to get the name from user. In this way up to 39 characters will be written, being the 40th reserved to the string terminator '\0'.

like image 142
Roberto Caboni Avatar answered Sep 30 '22 03:09

Roberto Caboni


As noted by Roberto in his answer, the problem is the %S conversion specifier, which is a typo, you should use %s.

Note however that there are other issues which might pose problems:

  • you should tell scanf() the maximum number of characters to read for the employee name, otherwise scanf() may write beyond the end of the destination array if input is too long.

  • if both programs run on separate systems with different endianness, the numbers will be incorrect on the receiving end because their bytes will be stored in the opposite order. For this reason, endianness should be specified and handled explicitly in binary files. Text format tends to be preferred for data transmission.

Here is a modified version:

//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>

int main() {
    FILE *fp;
    char another = 'Y';
    struct emp {
        char name[40];
        int age;
        float bs;
    } e;
    int c;

    fp = fopen("emp.bin", "wb");
    if (fp == NULL) {
        puts("Cannot open the file.");
        return 1;
    }
    while (another == 'Y') {
        printf("Enter the employee name, age and salary: ");
        if (scanf("%39s %d %f", e.name, &e.age, &e.bs) != 3)
            break;
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (fwrite(&e, sizeof(e), 1, fp) != 1)
            break;
        printf("Add another record? (Y/N)");
        another = getchar();
    }
    fclose(fp);
    return 0;
}

"Read" application code:

//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>

int main() {
    FILE *fp;
    struct emp {
        char name[40];
        int age;
        float bs;
    } e;
    fp = fopen("emp.bin", "rb");
    if (fp == NULL) {
        puts("Cannot open the file.");
        return 1;
    }
    while (fread(&e, sizeof(e), 1, fp) == 1) {
        printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
    }
    fclose(fp);
    return 0;
}
like image 28
chqrlie Avatar answered Sep 30 '22 04:09

chqrlie