Подтвердить что ты не робот

Правильный способ чтения текстового файла в буфер в C?

Я имею дело с небольшими текстовыми файлами, которые я хочу читать в буфере, пока я их обрабатываю, поэтому я придумал следующий код:

...
char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}
...

Является ли это правильным способом помещать содержимое файла в буфер или я злоупотребляю strcat()?

Затем я перебираю буфер таким образом:

for(int x = 0; (c = source[x]) != '\0'; x++)
{
    //Process chars
}
4b9b3361

Ответ 1

char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}

В этом коде есть несколько ошибок:

  • Это очень медленно (вы извлекаете буфер один символ за раз).
  • Если размер файла превышает sizeof(source), это подвержено переполнениям буфера.
  • В самом деле, когда вы смотрите на него более внимательно, этот код не должен работать вообще. Как указано в man-страницах:

Функция strcat() добавляет копию строки s2 с нулевым завершением в конец строки s1 с завершающим нулем, затем добавляет завершающий `\ 0 '.

Вы добавляете символ (а не строку с завершающим NUL!) в строку, которая может быть или не быть завершена NUL. Единственный раз, когда я могу представить, что это работает в соответствии с описанием man-страницы, является то, что каждый символ в файле заканчивается NUL, и в этом случае это было бы бессмысленно. Так что да, это, безусловно, ужасное злоупотребление strcat().

Ниже приведены две альтернативы для использования.

Если вы заранее знаете максимальный размер буфера:

#include <stdio.h>
#define MAXBUFLEN 1000000

char source[MAXBUFLEN + 1];
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp);
    if ( ferror( fp ) != 0 ) {
        fputs("Error reading file", stderr);
    } else {
        source[newLen++] = '\0'; /* Just to be safe. */
    }

    fclose(fp);
}

Или, если вы этого не сделаете:

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

char *source = NULL;
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    /* Go to the end of the file. */
    if (fseek(fp, 0L, SEEK_END) == 0) {
        /* Get the size of the file. */
        long bufsize = ftell(fp);
        if (bufsize == -1) { /* Error */ }

        /* Allocate our buffer to that size. */
        source = malloc(sizeof(char) * (bufsize + 1));

        /* Go back to the start of the file. */
        if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if ( ferror( fp ) != 0 ) {
            fputs("Error reading file", stderr);
        } else {
            source[newLen++] = '\0'; /* Just to be safe. */
        }
    }
    fclose(fp);
}

free(source); /* Don't forget to call free() later! */

Ответ 2

Да - вы, вероятно, будете арестованы за ваше терпимое злоупотребление strcat!

Взгляните на getline(), он считывает данные по строке за раз, но, что важно, он может ограничить количество символов, которые вы читаете, поэтому вы не переполняете буфер.

Strcat относительно медленный, потому что он должен искать всю строку для конца при каждой вставке символов. Обычно вы сохраняете указатель на текущий конец хранилища строк и передаете это, чтобы getline в качестве позиции для чтения следующей строки.

Ответ 3

Смотрите эту статью от JoelOnSoftware за то, почему вы не хотите использовать strcat.

Посмотрите fread для альтернативы. Используйте его с размером 1, когда вы читаете байты или символы.

Ответ 4

Почему бы вам просто не использовать массив символов, который у вас есть? Это должно сделать это:

   source[i] = getc(fp); 
   i++;

Ответ 5

Не тестировалось, но должно работать. И да, это может быть лучше реализовано с помощью fread, я оставлю это как упражнение для читателя.

#define DEFAULT_SIZE 100
#define STEP_SIZE 100

char *buffer[DEFAULT_SIZE];
size_t buffer_sz=DEFAULT_SIZE;
size_t i=0;
while(!feof(fp)){
  buffer[i]=fgetc(fp);
  i++;
  if(i>=buffer_sz){
    buffer_sz+=STEP_SIZE;
    void *tmp=buffer;
    buffer=realloc(buffer,buffer_sz);
    if(buffer==null){ free(tmp); exit(1);} //ensure we don't have a memory leak
  }
}
buffer[i]=0;

Ответ 7

Если вы работаете в Linux-системе, как только у вас есть файловый дескриптор, вы можете получить много информации о файле, используя fstat()

http://linux.die.net/man/2/stat

чтобы вы могли

#include  <unistd.h> 
void main()
{
    struct stat stat;
    int fd;
    //get file descriptor
    fstat(fd, &stat);
    //the size of the file is now in stat.st_size
}

Это позволяет избежать поиска начала и конца файла.