简体   繁体   中英

Random characters from memory when reading from file using fscanf

        //create the array from file
        char *array[100];
        char string[80];
        FILE * file;
        file = fopen( "file.txt" , "r");
        if (file) {
            int k = 0;
            while (fscanf(file, "%s", string)!=EOF){
                array[k] = strdup(string);
                k++;
            }                            
            fclose(file);
        }

        //print the history array for debugging
        for(int k = 0; k<sizeof(array); k++){
            printf("the element at %d is: %s\n", k, array[k]);
        } 

The resulting array has random characters from memory which are not present in the file. Is there any way to prevent this?

There are two problems in the code.

The first is that you are using fscanf to read the file, instead of fgets . fscanf with a format of "%s" will read one word from the file. fgets reads a line.

The second problem is the last for loop which uses sizeof(array) . On a 32-bit machine, sizeof(array) is 100*4 = 400. What you want is to count the number of lines that have been read from the file, and then use that count in the for loop.

With that in mind, here's how I would write the code

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

int main( void )
{
    char *array[100];
    char string[80];
    FILE *fp;

    if ( (fp = fopen( "file.txt" , "r")) == NULL )
    {
        printf( "File not found\n" );
        exit( 1 );
    }

    int count = 0;
    while ( fgets( string, sizeof(string), fp ) != NULL )
    {
        string[strcspn(string,"\n")] = '\0';
        if ( count < 100 )
        {
            array[count] = strdup( string );
            count++;
        }
    }

    fclose( fp );

    for ( int k = 0; k < count; k++ )
        printf( "the element at %d is: %s\n", k, array[k] );
}

The line

string[strcspn(string,"\n")] = '\0';

removes the newline, if any, from the string. This is necessary since fgets will retain the newline.

  1. If you want to read lines of text, use:

     fgets(line, sizeof(line), file); 
  2. If a line can contain 80 characters, you need to define line to be an array of at least 81 characters -- to allow an extra space for the terminating null character.

     char line[81]; 
  3. You need to store the number of lines read so that when writing the lines out, you don't try to write any more than you read. Make sure you don't read any more than the number of lines array is capable of holding.

     int numLinesRead = 0; if (file) { int k = 0; while (fscanf(file, "%s", string)!=EOF && k < 100 ){ array[k] = strdup(string); k++; } fclose(file); numLinesRead = k; } 
  4. Don't write any more than the number of lines read.

     for(int k = 0; k<numberOfLinesRead; k++){ printf("the element at %d is: %s\\n", k, array[k]); } 

There are several issues in this code ...

    char *array[100];
    char string[80];

so far, so good, now let's look at this:

        int k = 0;
        while (fscanf(file, "%s", string)!=EOF){
            array[k] = strdup(string);
            k++;
        }                            

Issues here:

  1. you don't check k . This should stop if k reached 100
  2. checking for EOF is wrong. Other things could fail, too.
  3. %s will only read up to the next whitespace, probably not what you want.
  4. %s will read as long as there are non-whitespace characters, so it will overflow your string , given malicious input.

A correct version would eg look like this:

        int k = 0;
        while (k < 100 && fgets(string, 80, file)){
            array[k] = strdup(string);
            k++;
        }                            

Then to the next problematic part:

    for(int k = 0; k<sizeof(array); k++){
        printf("the element at %d is: %s\n", k, array[k]);
    }

sizeof() gives you the size in bytes of its argument. The size of an array is fixed , it doesn't matter how many elements you actually stored. The elements are pointers, so taking more than 1 byte.

Correct version:

    int lines = 0; // nothing read
    if (file) {
        int k = 0;
        while (k < 100 && fgets(string, 80, file)){
            array[k] = strdup(string);
            k++;
        }                            
        fclose(file);
        lines = k;
    }

    for(int k = 0; k<lines; k++){
        printf("the element at %d is: %s\n", k, array[k]);
    }

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