Некоторые упомянули, что scanf
, вероятно, не подходит для этой цели. Я бы тоже не советовал использовать fgets
. Хотя он немного более удобен, есть проблемы, которых сложно избежать, по крайней мере на первый взгляд. Немногим программистам на C удается правильно использовать fgets
с первого раза, не прочитав руководство по fgets
в полном объеме. Части, которыми большинство людей полностью пренебрегают:
- что происходит, когда линия слишком велика, и
- что происходит, когда возникает
EOF
или ошибка.
Функция fgets()
должна считывать байты из stream
в массив, на который указывает s
, до тех пор, пока не будет прочитано n-1
байтов, или не будет прочитано и передано s
, или не будет обнаружено условие конца файла. Затем строка завершается нулевым байтом.
После успешного завершения fgets()
возвращает s
. Если поток находится в конце файла, должен быть установлен индикатор конца файла для потока, и fgets()
должен возвращать нулевой указатель. Если возникает ошибка чтения, должен быть установлен индикатор ошибки для потока, fgets()
должен возвращать нулевой указатель...
Я не чувствую необходимости слишком сильно подчеркивать важность проверки возвращаемого значения, поэтому я не буду упоминать об этом снова. Достаточно сказать, что если ваша программа не проверяет возвращаемое значение, ваша программа не будет знать, когда произойдет EOF
или произойдет ошибка; ваша программа, вероятно, застрянет в бесконечном цикле.
Когда '\n'
отсутствует, остальные байты строки еще не прочитаны. Таким образом, fgets
всегда будет анализировать строку хотя бы один раз внутри. Когда вы вводите дополнительную логику для проверки '\n'
, вы анализируете данные во второй раз.
Это позволяет вам realloc
хранить и снова вызывать fgets
, если вы хотите динамически изменить размер хранилища или отбросить оставшуюся часть строки (предупредить пользователя об усечении - хорошая идея), возможно, используя что-то вроде fscanf(file, "%*[^\n]");
.
Hugomg упомянул об использовании умножения в коде динамического изменения размера, чтобы избежать квадратичных проблем во время выполнения. В этом направлении было бы неплохо избегать многократного анализа одних и тех же данных на каждой итерации (таким образом создавая дополнительные квадратичные проблемы во время выполнения). Этого можно добиться, сохранив где-нибудь количество прочитанных (и проанализированных) байтов. Например:
char *get_dynamic_line(FILE *f) {
size_t bytes_read = 0;
char *bytes = NULL, *temp;
do {
size_t alloc_size = bytes_read * 2 + 1;
temp = realloc(bytes, alloc_size);
if (temp == NULL) {
free(bytes);
return NULL;
}
bytes = temp;
temp = fgets(bytes + bytes_read, alloc_size - bytes_read, f); /* Parsing data the first time */
bytes_read += strcspn(bytes + bytes_read, "\n"); /* Parsing data the second time */
} while (temp && bytes[bytes_read] != '\n');
bytes[bytes_read] = '\0';
return bytes;
}
Те, кому удастся прочитать руководство и придумать что-то правильное (например, это), могут вскоре понять, что сложность решения fgets
как минимум в два раза хуже, чем такое же решение с использованием fgetc
. Мы можем избежать повторного анализа данных, используя fgetc
, поэтому использование fgetc
может показаться наиболее подходящим. Увы, большинству программистов на C также удается использовать fgetc
неправильно, пренебрегая руководством fgetc
. .
Самая важная деталь — понять, что fgetc
возвращает int
, а не char
. Обычно он может возвращать одно из 256 различных значений от 0
до UCHAR_MAX
(включительно). В противном случае он может вернуть EOF
, что означает обычно существует 257 различных значений, которые fgetc
(или, следовательно, getchar
) может вернуть. Попытка сохранить эти значения в char
или unsigned char
приводит к потере информации, особенно режимов ошибок. (Конечно, это типичное значение 257 изменится, если CHAR_BIT
больше 8 и, следовательно, UCHAR_MAX
больше 255)
char *get_dynamic_line(FILE *f) {
size_t bytes_read = 0;
char *bytes = NULL;
do {
if ((bytes_read & (bytes_read + 1)) == 0) {
void *temp = realloc(bytes, bytes_read * 2 + 1);
if (temp == NULL) {
free(bytes);
return NULL;
}
bytes = temp;
}
int c = fgetc(f);
bytes[bytes_read] = c >= 0 && c != '\n'
? c
: '\0';
} while (bytes[bytes_read++]);
return bytes;
}
person
autistic
schedule
16.05.2015
strlen
для получения последнего индекса, предполагая, что ваша строка не имеет нуля в конце. Если в конце есть ноль, то зачем вы его повторно добавляете? - person ozdrgnaDiies   schedule 16.05.2015input
опечатка какstr
. - person BLUEPIXY   schedule 04.06.2017