Использование fgets () без предопределенного буфера - PullRequest
0 голосов
/ 27 июня 2018

Мне нужно задать еще один вопрос о чтении со стандартного ввода. Я читаю огромный ствол строк из стандартного ввода, но точно неизвестно, какой размер каждой строки. Поэтому я не хочу иметь буфер размером 50 миллионов для файла, содержащего строки по три символа, а не для файла, использующего эти 50 миллионов для каждой строки. Итак, на данный момент у меня есть этот код:

int cur_max = 2047;
char *str = malloc(sizeof(char) * cur_max);
int length = 0;

while(fgets(str, sizeof(str), stdin) != NULL) {
    //do something with str
    //for example printing
    printf("%s",str);
}

free(str);

Так что я использую fgets для каждой строки, и у меня есть первый размер 2047 символов в строке. Мой план состоит в том, чтобы увеличить размер буфера (str), когда строка достигает предела. Поэтому моя идея состоит в том, чтобы посчитать размер с длиной, и если текущая длина больше, чем cur_max, то я удваиваю cur_max. Идея приходит отсюда Чтение строки из файла, не зная длину строки В настоящее время я не уверен, как это сделать с помощью fgets, потому что я думаю, что fgets не использует этот тип char, поэтому я не знаю, когда увеличить размер.

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Неверный код

sizeof(str) - размер указателя, например, 2, 4 или 8 байтов. Передайте fgets() размер памяти, на который указывает str. @ Эндрю Хенле @ Стив Саммит

char *str = malloc(sizeof(char) * cur_max);
...
// while(fgets(str, sizeof(str), stdin) != NULL
while(fgets(str, cur_max, stdin) != NULL

Экологические ограничения

Текстовые файлы и fgets() не являются переносимым решением для чтения чрезмерно длинных строк.

Реализация должна поддерживать текстовые файлы со строками, содержащими не менее 254 символов, включая завершающий символ новой строки. Значение макроса BUFSIZ должно быть не менее 256 C11 §7.21.2 9

Так что, как только длина строки превысит BUFSIZ - 2, код сам по себе в отношении того, могут ли функции стандартной библиотеки C обрабатывать текстовый файл .

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

Примечание: BUFSIZ определено в <stdio.h>

0 голосов
/ 27 июня 2018

POSIX.1 getline() (man 3 getline) доступно почти во всех библиотеках C операционных систем (единственное исключение, о котором я знаю, это Windows). Цикл для чтения строк любой длины:

char    *line_ptr = NULL;
size_t   line_max = 0;
ssize_t  line_len;

while (1) {

    line_len = getline(&line_ptr, &line_max, stdin);
    if (line_len == -1)
        break;

    /* You now have 'line_len' chars at 'line_ptr',
       but it may contain embedded nul chars ('\0').
       Also, line_ptr[line_len] == '\0'.
    */
}

/* Discard dynamically allocated buffer; allow reuse later. */
free(line_ptr);
line_ptr = NULL;
line_max = 0;

Существует также связанная функция getdelim(), которая принимает дополнительный параметр (указанный перед потоком), используемый в качестве маркера конца записи. Это особенно полезно в средах Unixy / POSIXy при чтении имен файлов, например, из стандартный ввод, так как вы можете использовать сам nul ('\0') в качестве разделителя (см., например, find -print0 или xargs -0), что позволяет правильно обрабатывать все возможные имена файлов.

Если вы используете Windows или если у вас есть текстовые файлы с различными соглашениями новой строки (не только '\n', но любой из '\n', '\r', "\r\n" или "\n\r"), вы можете использовать мой getline_universal() Реализация функции из другого моего ответа. Он отличается от стандартных getline() и fgets() тем, что новая строка не включена в возвращаемую строку; он также остается в потоке и используется / игнорируется вызовом next на getline_universal(). Если вы используете getline_universal() для чтения каждой строки в файле или потоке, он будет работать как положено.

...