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

时间:2017-01-15 11:40:17

标签: c file io c11

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

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' 
        && _ != '\0' &&  _ != '\r' && _ != EOF) {
       buffer[ct++] = _;
   }

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

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

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

有没有办法动态地改变数组的大小,具体取决于当前的迭代而不总是uisng realloc?或者有没有办法通过ftellfseek来确定当前行的持续时间?

3 个答案:

答案 0 :(得分:0)

如果您的文件无法适合内存,则无法在内存中使用。您正在预先分配内存缓冲区,但是您犯了两个错误,导致您分配的内容超出了您的需要。

  1. 您从文件中的某个任意位置开始,但分配内存就好像您从文件的开头开始一样。分配ftell(file) - threshold个字节。
  2. 你是分配方式太多的记忆。 sizeof(uint8_t *)应该是sizeof(uint8_t)。你分配的内存比应该多4或8倍。
  3. 除此之外,在完成写入之后重新分配缓冲区的重点是什么?内存溢出已经发生。你应该在写之前分配(在while循环中)。但是,我根本没有看到重新分配的重点,因为你开始分配的内存超过了。

答案 1 :(得分:0)

代码不会返回指向字符串的指针。

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

重新分配时,添加1。

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

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

有没有办法动态改变数组 分配的内存的大小,具体取决于当前的迭代而不总是使用realloc

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

  

或者有没有办法使用ftellfseek确定当前行的时长?

与此代码一样,您已找到当前行长度的上限。 ftellfseek找不到行尾。

代码可以使用fscanf(file, "%*[^\n]");“追踪”到fgetc(file)或1以后的行尾。

答案 2 :(得分:0)

以下代码:

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

    #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 != '\0'
            &&  ch != '\r'
            &&  ch != EOF)
       {
           buffer[ct++] = (char)ch;
       }
    
       return buffer;
    } // end function: fs_read_line