Получение данных из файлового потока в c - PullRequest
2 голосов
/ 12 апреля 2011

Я пытаюсь получить данные из указателя ФАЙЛА в строку.Каков наилучший способ определения размера буфера строк?

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

fgets(string, sizeof string, fp);

Нужно ли устанавливать размер буфера, который, по моему мнению, подходит для данного файла?Или есть более эффективный способ сделать это без использования строк с не переменным размером буфера?

Ответы [ 4 ]

2 голосов
/ 12 апреля 2011

В общем, вам просто нужно выбрать размер и пойти с ним. Выбор основывается на максимальной ожидаемой длине строки или длине записи или чем-то подобном, специфичному для типа ввода. Просто проверьте коды возврата и обработайте случай, когда строка длиннее, чем вы ожидаете.

Есть несколько трюков, которые вы могли бы сыграть, чтобы получить точный размер, однако я не помню, чтобы мне приходилось когда-либо использовать их на практике:

  1. Сделайте ftell, читайте символ за символом, считайте до тех пор, пока не достигнете новой строки, затем выделите достаточно памяти, fseek для перемотки и прочитайте всю строку.

  2. Сделайте fseek до конца файла, чтобы найти размер, затем перемотайте и прочитайте все сразу в одном буфере.

1 голос
/ 12 апреля 2011

Простым и понятным способом является использование 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;
}
0 голосов
/ 12 апреля 2011

Если вы на самом деле намереваетесь прочитать строк из файла (что является обычной причиной использования fgets вместо, скажем, fread), тогда вам нужно, чтобы буфербыть достаточно длинным, чтобы провести линию.Зачастую вы не можете знать об этом заранее, поэтому выделите его динамически с помощью malloc (или new, если вы используете C ++, хотя в этом случае вам лучше использовать средства ввода-вывода C ++) и увеличьте его, когдаты пересекаешь слишком длинную линию.Примерно так:

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 ++, то вам, вероятно, следует использовать средства, которые он уже предоставляет для этого.)

0 голосов
/ 12 апреля 2011

Одна возможность состоит в том, чтобы динамически выделить буфер и затем увеличить его (например, с помощью realloc) по мере необходимости. Это, вероятно, потребует написания функции-обертки для fgets, которая будет проверять, прочитала ли она всю строку (символ новой строки сохраняется в буфере). Это также должно обрабатывать условия EOF.

Это, вероятно, само собой разумеется, но использование C для чтения и анализа текстовых файлов с данными переменной ширины - довольно большая работа. Это может не иметь смысла для вашей ситуации или даже быть возможным, но если вы можете использовать что-то вроде Ruby, Python, Perl, Awk и т. Д., Вы, вероятно, сможете выполнить задачу за короткое время. Вы можете выполнить несколько строк кода с помощью этих инструментов, которые могут занимать сто строк кода C. Они идеально подходят для чтения и анализа текстового файла с разделителями. Например, следующий фрагмент ruby ​​читает текстовый файл построчно и разбивает его на вертикальные полосы:

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