简体   繁体   中英

Issue with fread () and fwrite () functions in C

I have written a basic code which writes into a file a string in binary mode (using fwrite()). Also I can read the same string from the file (using fread()) in to the buffer and print it. It works but in the part where I read from the file, extra junk is also read into the buffer. My question is how to know the length of the bytes to be read, correctly?

The following is the code --

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

#define BUFSZ 81

char * get_string (char *, size_t);

int main (int argc, char * argv[])
{
    if (argc != 2)
    {
        fprintf (stderr, "Invalid Arguments!!\n");
        printf ("syntax: %s <filename>\n", argv[0]);
        exit (1);
    }

    FILE * fp;

    if ((fp = fopen(argv[1], "ab+")) == NULL)
    {
        fprintf (stderr, "Cannot openm file <%s>\n", argv[1]);
        perror ("");
        exit (2);
    }


    char string[BUFSZ];
    char readString[BUFSZ];
    size_t BYTES, BYTES_READ;

    puts ("Enter a string:  ");
    get_string (string, BUFSZ);

    // printf ("You have entered:  %s\n", string);

    BYTES = fwrite (string, sizeof (char), strlen (string), fp);

    printf ("\nYou have written %zu bytes to file <%s>.\n", BYTES, argv[1]);

    printf ("\nContents of the file <%s>:\n", argv[1]);

    rewind (fp);

    BYTES_READ = fread (readString, sizeof (char), BUFSZ, fp);
    printf ("%s\n", readString);

    printf ("\nYou have read %zu bytes from file <%s>.\n", BYTES_READ, argv[1]);    

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


char * get_string (char * str, size_t n)
{
    char * ret_val = fgets (str, n, stdin);
    char * find;

    if (ret_val)
    {
        find = strchr (str, '\n');
        if (find)
            * find = '\0';
        else
            while (getchar () != '\n')
                continue; 
    }

    return ret_val;
}

in the part where I read from the file, extra junk is also read into the buffer.

No, it isn't. Since you're opening the file in append mode, it's possible that you're reading in extra data preceding the string you've written, but you are not reading anything past the end of what you wrote, because there isn't anything there to read. When the file is initially empty or absent, you can verify that by comparing the value of BYTES to the value of BYTES_READ .

What you are actually seeing is the effect of the read-back data not being null terminated. You did not write the terminator to the file, so you could not read it back. It might be reasonable to avoid writing the terminator, but in that case you must supply a new one when you read the data back in. For example,

readString[BYTES_READ] = '\0';

My question is how to know the length of the bytes to be read, correctly?

There are various possibilities. Among the prominent ones are

  • use fixed-length data
  • write the string length to the file, ahead of the string data.

Alternatively, in your particular case, when the file starts empty and you write only one string in it, there is also the possibility of capturing and working with how many bytes were read instead of knowing in advance how many should be read.

First of all you get string from the user, which will contain up to BUFSZ-1 characters ( get_string() function will remove the trailing newline or skip any character exceeding the BUFSZ limit.

For example, the user might have inserted the word Hello\\n , so that after get_string() call string array contains

-------------------
|H|e|l|l|o|'\0'|...
-------------------

Then you fwrite the string buffer to the output file, writing strlen (string) bytes. This doesn't include the string terminator '\\0' .

In our example the contents of the output file is

--------------
|H|e|l|l|o|...
--------------

Finally you read back from the file. But since readString array is not initialized, the file contents will be followed by every junk character might be present in the uninitialized array.

For example, readString could have the following initial contents:

---------------------------------------------
|a|a|a|a|a|T|h|i|s| |i|s| |j|u|n|k|!|'\0'|...
---------------------------------------------

and after reading from the file

---------------------------------------------
|H|e|l|l|o|T|h|i|s| |i|s| |j|u|n|k|!|'\0'|...
---------------------------------------------

So that the following string would be printed

HelloThis is junk!

In order to avoid these issues, you have to make sure that a trailing terminator is present in the target buffer. So, just initialize the array in this way:

char readString[BUFSZ] = { 0 };

In this way at least a string terminator will be present in the target array.

Alternatively, memset it to 0 before every read:

memset (readString, 0, BUFSZ);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM