使用 fscanf 从文件读取时内存中的随机字符

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]);
        } 

生成的数组包含内存中的随机字符,这些字符不存在于文件中。有什么办法可以防止这种情况吗?

代码中有两个问题。

首先是您使用 fscanf 读取文件,而不是 fgetsfscanf 格式为 "%s" 将从文件中读取一个单词。 fgets 读取一行。

第二个问题是最后一个 for 循环,它使用了 sizeof(array)。在 32 位机器上,sizeof(array) 是 100*4 = 400。您想要的是计算从文​​件中读取的行数,然后在 for 循环中使用该计数.

考虑到这一点,下面是我编写代码的方式

#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")] = '[=10=]';
        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] );
}

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

从字符串中删除换行符(如果有)。这是必要的,因为 fgets 将保留换行符。

这段代码中有几个问题...

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

到目前为止一切顺利,现在让我们看看这个:

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

这里的问题:

  1. 你不检查 k。如果 k 达到 100
  2. ,这应该停止
  3. 检查 EOF 是错误的。其他事情也可能失败。
  4. %s 只会读取下一个空格,可能不是您想要的。
  5. 只要有非空白字符,
  6. %s 就会读取,因此它 溢出你的 string,给出恶意输入。

正确的版本应该是看起来像这样:

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

然后进入下一个有问题的部分:

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

sizeof() 为您提供其参数的 字节大小 。一个数组的大小是固定的,不管你实际存储了多少个元素。元素是指针,所以占用1个多字节。

正确版本:

    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]);
    }
  1. 如果要阅读文本行,请使用:

    fgets(line, sizeof(line), file);
    
  2. 如果一行可以包含 80 个字符,则需要将 line 定义为至少包含 81 个字符的数组——以允许额外的space 用于终止空字符。

    char line[81];
    
  3. 您需要存储读取的行数,以便在写出这些行时,您不会尝试写入比读取更多的内容。请确保您阅读的行数不超过 array 能够容纳的行数。

    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. 写的行数不要超过阅读的行数。

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