从c中获取文件流中的数据

时间:2011-04-12 13:00:38

标签: c buffer filestream

我试图将数据从FILE指针和字符串中检索出来。确定字符串缓冲区大小的最佳方法是什么?

char string[WHAT_SIZE?];
FILE *fp;
fp = fopen("info.dat", "r");

fgets(string, sizeof string, fp);

我是否将缓冲区大小设置为我认为适合该特定文件的大小?或者,如果不使用具有非可变缓冲区大小的字符串,是否有更有效的方法来执行此操作?

4 个答案:

答案 0 :(得分:2)

一般来说,你只需选择一个尺码并随身携带。基于最大预期行长度或记录长度的选择或类似于输入类型的选择。只需确保检查返回代码并在行超出预期时处理案例。

你可以用一些技巧来获得一个确切的大小,但是我记不起在实践中不得不使用这些技巧:

  1. 做一个ftell,通过char读取char,计算直到你到达换行符,然后分配足够的内存,fseek倒带,并读取整行。

  2. 在文件末尾执行fseek以查找大小,然后将整个内容倒回并一次读入单个缓冲区。

答案 1 :(得分:1)

简单直接的方法是使用fseek()ftell()。检索文件大小后,为数据分配缓冲区并使用fread()读取文件。

此示例是检索文件大小的常用方法。

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

/* excepts file stream which is already opened */
long get_filesize(FILE *fp)
{
    long filesize;

    if( fseek(fp, 0, SEEK_END) ) != 0)
        exit(EXIT_FAILURE); /* exit with errorcode if fseek() fails */

    filesize = ftell(fp);

    rewind(fp);

    return filesize;
}

int main(void)
{
    FILE *fp;
    long filesize;
    unsigned char *buffer;

    fp = fopen("info.dat", "rb");

    filesize = get_filesize(fp);
    if(filesize < 1) exit(EXIT_FAILURE);

    buffer = malloc( filesize * sizeof(unsigned char) );
    if(buffer == NULL) exit(EXIT_FAILURE);

    /* checking the fread return value is not necessary but recommended */
    if((fread(buffer, sizeof(unsigned char), filesize, fp)) != filesize)
        exit(EXIT_FAILURE);

    fclose(fp);

    /* ===== use the file here ===== */

    free(buffer); /* remember to free the memory */

    return EXIT_SUCCESS;
}

答案 2 :(得分:0)

一种可能性是根据需要动态分配缓冲区然后增长它(例如,使用realloc)。这可能需要为fgets编写一个包装函数,以检查它是否读取整行(换行符存储在缓冲区中)。它还必须处理EOF条件。

这可能不言而喻,但使用C来读取和解析具有可变宽度数据的文本文件是相当多的工作。它可能对你的情况没有意义甚至是可能的,但是如果你可以使用像Ruby,Python,Perl,Awk等那样的东西,你可以在很短的时间内完成任务。您可以使用那些可能需要100行C的工具完成几行代码。它们非常适合读取和解析分隔的文本文件。例如,下面的红宝石块逐行读取文本文件并用垂直条分开:

File.open("myfile.txt") { |file|
   while ( line = file.gets )
       puts "line: #{line}"
       a = line.split( /\|/ )
       puts "array: #{a}"
   end
}

只是为了好玩,这是一个可能的实现,有几个待处理的TBD(错误检查)。主要问题(除了我没有看到的微妙错误)将解决如果你没有完全读取EOF而释放缓冲区的问题。

int myReadLine   // return non-zero if line returned, 0 on eof (see tbd below)
(
   FILE *fp,     // (I) open file handle for reading
   char **buf,   // (IO) buffer allocated by this function.  It is freed by
                 // this function when EOF is hit.  TBD: Should write a myFreeLine
                 // (for encapsulation purposes) to free this buffer for cases where
                 // you quit calling
   int  *len     // (IO) current length of buffer pointed to by buf
)
{
   char *ret;
   char *pos;
   int  curlen;
   int  remaining;

   if ( *len == 0 )
      {
      assert( *buf == NULL );
      // pick a number out of the air.  Could be app-specific.  In debug
      // it may be nice to start very small to force reallocs to exercise all
      // code paths.
      *len = 2;
      // tbd: need error checking
      *buf = (char*)malloc( *len * sizeof( char ));
      }

   pos = *buf;
   remaining = *len;

   while ( 1 )
      {
      ret = fgets( pos, remaining, fp );
      if ( ret == NULL )
         {
         // tbd: should check if error occurred here.  For now assuming eof
         free( *buf );
         *buf = NULL;
         *len = 0;
         return 0;
         }

      // check to see if we got the entire line.
      curlen = strlen( *buf );
      if ( (*buf)[curlen - 1] == '\n' )  // tbd:  check for \r?
         {
         // apparently we got the whole line
         // remove the end of line (at least that's what I would want)
         (*buf)[curlen - 1] = '\0';
         return 1;
         }
      else
         {
         // failed to get entire line
         assert( curlen + 1 == *len );

         // grow the buffer (tbd: realloc is a pain ... need error checking)
         *len *= 2;  // doubling is often a good plan
         *buf = (char*)realloc( *buf, *len );

         // set the "amount left" variables correctly for next iteration
         remaining = *len - curlen;
         pos = *buf + curlen;
         }
      }  // while forever

   // don't expect to get here
   assert( 0 );

}

这是一个示例电话:

void readfile(char *filepath)
{
   int len = 0;
   char *buf = NULL;


   FILE *fp=fopen(filepath,"rt");
   while ( myReadLine( fp, &buf, &len  ))
      printf( "'%s'\n", buf );
   fclose(fp);
}

答案 3 :(得分:0)

如果你真的打算从文件中读取(这是使用fgets而不是fread)的常用原因,那么你需要什么是缓冲区足够长以容纳一条线。您通常无法提前知道,因此使用malloc(或new动态分配它,如果您使用的是C ++,但在这种情况下,您可能会更好地使用C ++的I / O工具)当你跑过一条太长的线时放大它。像这样:

size_t line_size = 256; /* reasonable initial default */
char * line_buffer = malloc(line_size);
line_buffer[line_size-2] = '\n'; /* yes, 2 */
/* You should check for malloc failure here */
while (whatever) {
  /* ... */
  fgets(line_buffer, line_size, fp); /* should check for failure and EOF here too */
  while (line_buffer[line_size-2] != '\n') {
    /* we filled the buffer, and the last character wasn't a newline */
    size_t new_line_size = 2*line_size;
    line_buffer = realloc(line_buffer, new_line_size); /* should check for failure here */
    line_buffer[new_line_size-2] = '\n';
    fgets(line_buffer+line_size-1, new_line_size-line_size+1, fp); /* should check for failure and EOF */
    line_size = new_line_size;
  }
  /* ... */
}

(警告:完全未经测试的代码;可能完全由错误和有毒废物组成。当然没有真正代码应该具有的错误条件的所有测试。)

你可能会建议不要让缓冲区无限制地增长,如果有些白痴喂你一个长篇疯狂的文件;在某些时候放弃。您可能希望将上述行为封装到函数中,特别是如果您有多个代码执行相同的操作。在这种情况下,您可能还希望将其状态(缓冲区及其当前大小)封装到struct中。 (或者,如果你正在使用C ++,一个类,其中缓冲区扩展读取的东西将是一个成员函数。但是,再次,如果你使用C ++那么你应该使用它已经提供的设施。)