通过 fread 读取变量并通过 sscanf 存储

Reading variables via fread and storing via sscanf

我有一个 .txt 文件,其中包含一些变量的值。我需要阅读它们以在 main 中声明我的变量。怎么了?

#include <stdio.h>
#define INPUT "input.txt"

int main (void){

    FILE *open_file = fopen(INPUT, "r");

    if (open_file == NULL){
        puts("ERROR.");
    } else {
        puts("SUCCESS.");
    }

    char *buffer;
    int size = ftell(open_file); 
    int read_file = fread(buffer,size,*open_file);
    int Integer1, Integer2;
    while (size != EOF){
        sscanf("%d%d",Integer1, Integer2);
    }


    int close_file = fclose(open_file);
    if (close_file == -1){
        puts("Error in closing file.");
    } else {
        puts("Closing file: SUCCESS.");
    }


    return 0;
}

怎么了?我必须阅读文件的每一行。例如,如果我的文件包含:

1
2

我的 scanf 应该设置:

Integer1 = 1; 
Integer2 = 2;

您的代码中有几个问题

  1. fread(buffer,size,*open_file);,buffer未分配

    A) 如果使用指针,则必须使用 malloccalloc 分配内存,并在完成后释放内存。
    如果你想避免分配和释放的麻烦,你最好使用一个足以存储内容的数组。

  2. fread 接受 4 个参数

    A) 第四个参数不是 FILE ,它是 FILE* ,只使用 open_file 而不是 *open_file 你有未使用 nmemb(成员数)参数
    成功时,读取的项目数 fread() return,因此请检查 return 值以避免错误。

  3. ftell() returns当前偏移量,否则-1为returned
    A)你真的不需要它,检查fread的return值就知道你已经到了EOF。

  4. 检查sscanf and example

    的语法

一个问题是行后

char *buffer;

变量buffer没有指向任何有效的内存位置。这是一个 wild pointer.

您可以像这样创建一个固定大小的数组:

char buffer[100];

或者您可以创建动态大小的内存缓冲区,如下所示:

if ( fseek( open_file, 0, SEEK_END ) != 0 )
    DoSomethingToHandleError();
long size = ftell(open_file);
if ( fseek( open_file, 0, SEEK_SET ) != 0 )
    DoSomethingToHandleError();
char *buffer = malloc( size );
if ( buffer == NULL )
    DoSomethingToHandleError();

根据您使用的是固定大小的数组还是动态分配的缓冲区,您应该将对 fread 的调用更改为以下之一:

fread( buffer, sizeof(buffer), open_file ); //for fixed size array

fread( buffer, size, open_file ); //for dynamically allocated buffer

与其使用函数 fread,不如使用函数 fgets,尤其是因为 sscanf 需要以空字符结尾的字符串作为输入,而不是二进制数据.函数 fread 将为您提供不以 null 结尾的二进制数据,而 fgets 将为您提供以 null 结尾的字符串。

另外,换行

sscanf("%d%d",Integer1, Integer2);

sscanf( buffer, "%d%d", &Integer1, &Integer2);

在使用 Integer1Integer2 之前,您还应该检查 sscanf 的 return 值以确保在字符串中找到两个整数。

然而,如果你不想自己处理内存管理和文件的读入,你可以简单地使用fscanf,像这样:

if ( fscanf( open_file, "%d%d", &Integer1, &Integer2 ) != 2 )
    DoSomethingToHandleError();

但这有一个缺点,即 fscanf 只会给你它在文件中找到的前两个数字,并且不会执行太多的输入验证。例如,fscanf 不会强制这两个数字在不同的行上。有关使用 fscanf 的缺点的更多信息,请参阅以下 link:

A beginners' guide away from scanf()

如果您按照我的建议使用函数 fgets,那么您将需要两次调用 fgets 来读取文件的两行。这意味着 sscanf 将无法在同一个字符串中找到两个整数。因此,如果您使用 fgets,那么您将不得不稍微更改您的程序逻辑,例如:

#define MAX_INTEGERS 2
char buffer[100];
int integers[MAX_INTEGERS];
int num_found = 0;

while ( fgets( buffer, sizeof(buffer), open_file ) != NULL )
{
    int i;

    if ( strchr( buffer, '\n' ) == NULL && !feof( openfile ) )
    {
        printf( "Error: Line size too long! Aborting.\n" );
        break;
    }

    if ( sscanf( buffer, "%d", &i ) == 1 )
    {
        if ( num_found == MAX_INTEGERS )
        {
            printf(
                "Error: Found too many integers in file! This "
                "program only has room for storing %d integers. "
                "Aborting.\n",
                MAX_INTEGERS
            );
            break;
        }

        //add found integer to array and increment num_found
        integers[num_found++] = i;
    }
    else
    {
        printf(
            "Warning: Line without number encountered. This could "
            "simply be a harmless empty line at the end of the "
            "file, but could also indicate an error.\n"
        );
    }
}

printf( "Found the following integers:\n" );

for ( int i = 0; i < num_found; i++ )
{
    printf( "%d\n", integers[i] );
}

您可能想使用 strtol 而不是使用 sscanf,因为该函数允许您执行更严格的输入验证。

如果您不想使用逐行读取输入的函数 fgets,但又想使用 fread 一次读取整个文件,您可以这样做,但您必须手动添加终止空字符。


编辑:由于您在评论中声明您不想要循环并希望将每一行存储到它们自己的命名变量中,因此您可以改用以下代码:

//This function will assume that one number is stored per line, and
//write it to the memory location that "output" points to. Note that
//the return value does not specify the number found, but rather
//whether an error occurred or not. A return value of 0 means no error,
//nonzero means an error occurred.

int get_number( FILE *fp, int *output )
{
    char buffer[100];
    int i;

    //read the next line into buffer and check for error
    if ( fgets( buffer, sizeof(buffer), fp ) == NULL )
        return -1;

    //make sure line was not too long to fit into buffer
    if ( strchr( buffer, '\n' ) == NULL && !feof( fp ) )
        return -1;

    //parse line and make sure that a number was found
    if ( sscanf( buffer, "%d", &i ) != 1 )
        return -1;

    *output = i
    return 0;
}

现在,在您的函数 main 中,您可以简单地使用以下代码:

int error_occurred = 0;

if ( get_number( &Integer1 ) != 0 )
{
    printf( "Error occured when reading the first integer.\n" );
    error_occurred = 1;
}

if ( get_number( &Integer2 ) != 0 )
{
    printf( "Error occured when reading the second integer.\n" );
    error_occurred = 1;
}

if ( !error_occurred )
{
    printf( "The value of Integer1 is: %d\n", Integer1 );
    printf( "The value of Integer2 is: %d\n", Integer2 );
}

OP 的代码无法找到数组的末尾,为 buffer 分配了 space,并且错误地使用了 fread()
显示了一些错误检查的正确版本。

//char *buffer;
//int size = ftell(open_file); 
//int read_file = fread(buffer,size,*open_file);

// Seek and report, hopefully, the end position
if (fseek(open_file, 0, SEEK_END)) { fprintf(stderr, "fseek() failed.\n"); return EXIT_FAILURE;
long size = ftell(open_file); 
if (size == -1) { fprintf(stderr, "ftell() failed.\n"); return EXIT_FAILURE; }

// Allocate memory and read
if ((unsigned long) size > SIZE_MAX) { fprintf(stderr, "size too big.\n"); return EXIT_FAILURE; }
char *buffer = malloc((size_t) size);
if (buffer == NULL) { fprintf(stderr, "malloc() failed.\n"); return EXIT_FAILURE; }
size_t length = fread(buffer, sizeof *buffer, size, open_file);

// Use `length` as the number of char read
// ...
// when done
free(buffer);

还有其他问题。

// while (size != EOF){
//    sscanf("%d%d",Integer1, Integer2);
// }

也许稍后,GTG。