Някои споменаха, че scanf
вероятно не е подходящ за тази цел. Аз също не бих предложил да използвате fgets
. Въпреки че е малко по-подходящ, има проблеми, които изглеждат трудни за избягване, поне в началото. Малко C програмисти успяват да използват fgets
правилно от първия път, без да прочетат ръководството за fgets
изцяло. Частите, които повечето хора успяват да пренебрегнат изцяло, са:
- какво се случва, когато линията е твърде голяма и
- какво се случва, когато се срещне
EOF
или грешка.
Функцията fgets()
ще чете байтове от stream
в масива, посочен от s
, докато не бъдат прочетени n-1
байта, или a е прочетено и прехвърлено към 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