使用 "fgetc" 读取文件时出错(溢出)

Error when reading a file with "fgetc" (Overflow)

我正在使用此代码读取文件:

char* fs_read_line(FILE* file)
{
   if (file == NULL) {
       return "CFILEIO: Error while reading the file: Invalid File";
   }

   long threshold = ftell(file);
   fseek(file, 0, SEEK_END);
   uint8_t* buffer = calloc(ftell(file)-threshold, sizeof(uint8_t));

   if(buffer == NULL)
      return;

   int8_t _;
   fseek(file, threshold, SEEK_SET);

   uint32_t ct = 0;
   while ((_ = (char)(fgetc(file))) != '\n' 
        && _ != '[=11=]' &&  _ != '\r' && _ != EOF) {
       buffer[ct++] = _;
   }

   buffer = realloc(buffer, sizeof *buffer * (ct + 1)); 
   buffer[ct] = '[=11=]';
   return buffer;
}

如果文件太大,我会收到(堆)溢出错误,可能是因为我最初分配文件时包含的字符总数。

我尝试这样做的另一种方法是 realloc 每次迭代后的缓冲区,但这有点不是我想要的方法。

有什么方法可以根据当前迭代动态更改数组的大小而不总是 uisng realloc 吗?或者有没有办法通过使用 ftellfseek?

来确定当前行的长度

如果您的文件无法放入内存,那么它也无法放入内存。您正在提前分配内存缓冲区,但您犯了两个错误,这可能会导致您分配的内存超出您的需要。

  1. 您从文件中的任意位置开始,但分配内存就像从文件开头开始一样。分配 ftell(file) - threshold 字节。
  2. 你分配的内存太多了。 sizeof(uint8_t *) 应该改为 sizeof(uint8_t)。您分配的内存比应有的多 4 或 8 倍。

除此之外,在您写完之后重新分配缓冲区有什么意义?内存溢出已经发生。您应该在写入之前分配(在 while 循环内)。不过,我根本看不到重新分配的意义,因为您分配的内存已经足够多了。

代码不return指向字符串的指针。

returned buffer 中没有 空字符,因此调用代码无法知道已分配内存的长度。这肯定会导致调用代码出错。

当re-allocating时,加1。

// buffer = realloc(buffer, ct * sizeof(uint8_t*));
//                                          v--- no star
buffer = realloc(buffer, ct * sizeof(uint8_t ) + 1);
buffer[ct] = '[=10=]';

// or better
size_t ct = 0;
...
buffer = realloc(buffer, sizeof *buffer * (ct + 1));
buffer[ct] = '[=10=]';

Is there any way to dynamically change the size of the array allocated memory depending on the the current iteration without always using realloc?

数组大小无法更改。要动态更改已分配内存的大小需要 realloc()。注意:所需的内存量可以在内存分配调用之前确定。

or is there an way to determine how long the current line is by using ftell and fseek?

像这段代码一样,您找到了当前行长度的上限。 ftellfseek 没有找到行尾。

代码可以 "seek" 到行尾 fscanf(file, "%*[^\n]"); 或 1 之后的 fgetc(file).

以下代码:

  1. 干净地编译
  2. 执行所需的操作
  3. 正确处理错误情况
  4. 正确声明变量类型
  5. 正确地 return 是 char* 而不是 uint8_t*
  6. 留下悬而未决的问题:为什么 return 是所需缓冲区长度的 2 倍
  7. 传入参数为NULL时显示的错误信息不正确。建议更改以指示传入的文件指针为 NULL
  8. OP 发布的代码无法检查每次调用 fseek() 的 returned 值,也无法检查每次调用 ftell() 的 return 值应该这样做以确保操作成功。我没有在我的答案中添加错误检查,以免代码混乱,但是,应该执行它。

现在,代码:

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


char* fs_read_line(FILE* file);


char* fs_read_line(FILE* file)
{
   if ( !file )
   {
       return "CFILEIO: Error while reading the file: Invalid File";
   }

   // implied else, valid parameter

   long threshold = ftell(file);
   fseek(file, 0, SEEK_END);

   char* buffer = calloc( (size_t)(ftell(file) - threshold) *2 +1, sizeof(char));
   if(buffer == NULL)
      return NULL;

   // implied else, calloc successful

   int ch;
   fseek(file, threshold, SEEK_SET);

   size_t ct;
   while ( (ch = fgetc(file)) != '\n'
        &&  ch != '[=10=]'
        &&  ch != '\r'
        &&  ch != EOF)
   {
       buffer[ct++] = (char)ch;
   }

   return buffer;
} // end function: fs_read_line