Как использовать scanf и fgets для чтения файла
Мне нужно прочитать следующий текстовый файл:
2 2
Kauri tree
Waterfall
0 0 W S
0 1 E N
Я хотел использовать scanf
чтобы получить первую строку и использовать fgets
для второй и третьей строки затем используйте scanf
снова для остальных строк.
Я написал код так:
#include <stdio.h>
#define NUM_OF_CHAR 2
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char* str[NUM_OF_CHAR]; //should be char str[NUM_OF_CHAR];
for (int i = 0; i < node; i++) {
fgets(str[i], 2, fp); //should be fgets(str, 2, fp);
}
printf("%s", str[0]); //printf("%s", str);
}
ввод, который я ввел, был:
2 2
hello
я получил Segmentation fault
Я видел подобный вопрос здесь, человек упомянул, что я могу позвонить fgets
один раз для получения первой строки, но проигнорируйте ее и затем используйте fgets
снова, чтобы получить вторую строку. Но я не знаю, как это сделать.
3 ответа
Локальные переменные, определенные внутри функций, будут иметь, если явно не инициализировано, неопределенное значение. Для указателей это означает, что они указывают на, казалось бы, случайное местоположение. Использование любой неинициализированной переменной, кроме как для ее инициализации, приводит к неопределенному поведению.
Что здесь происходит, так это fgets
будет использовать (неинициализированный и, казалось бы, случайный) указатель и использовать его для записи в память, на которую он указывает. Эта память в большинстве случаев не принадлежит вам или вашей программе и может даже перезаписать некоторые другие важные данные. Это может привести к сбоям или другому странному поведению или результатам.
Самое простое решение - сделать str
массив массивов символов, например
#define NUM_OF_STRINGS 2
#define STRING_LENGTH 64
...
char str[NUM_OF_STRINGS][STRING_LENGTH];
...
fgets(str[i], sizeof str[i], stdin);
Вы должны убедиться, что STRING_LENGTH
выше достаточно, чтобы соответствовать каждой строке, включая символ новой строки и терминатор строки. В случае того, что я показываю выше с этим 64
это означает, что вы можете иметь строки не более 62 символов.
Теперь что касается другой проблемы, которую я указал, с первым вызовом fgets
чтение пустой строки
Если у вас есть вход
2 2
hello
Ввод сохраняется в буфере в памяти, а затем scanf
а также fgets
читает из этого буфера. Буфер с приведенным выше вводом будет выглядеть примерно так
+----+----+----+----+----+----+----+----+----+ | 2 | 2 | \n | ч | е | л | л | о | \n | +----+----+----+----+----+----+----+----+----+
После scanf
вызов прочитать два числа, как выглядит входной буфер
+----+----+----+----+----+----+----+ | \n | ч | е | л | л | о | \ n | + ---- + ---- + ---- + ---- + ---- + ---- + ---- +
Так что самый первый звонок fgets
в цикле увидим это новая строка. Таким образом, он читает эту новую строку, а затем все готово, оставляя строку "hello\n"
в буфере для второго вызова fgets
,
Есть несколько способов решить эту проблему. То, что я лично предпочитаю, это использовать fgets
универсально читать строки, и если вам нужен простой анализ строки, то используйте sscanf
(обратите внимание на ведущие s
Также, пожалуйста, смотрите здесь для хорошей справки всех scanf
варианты) для этого.
Другой способ - просто читать символы из ввода, по одному символу за раз, и отбрасывать их. Когда вы читаете новую строку, остановите цикл и продолжите работу с остальной частью программы.
Рассмотрим следующий пример, где комментарии объясняют некоторые важные моменты:
#include <stdio.h>
#define NUM_OF_CHAR 2
#define LEN_OF_STR 20
int main()
{
int node, edge;
FILE* fp;
fp = stdin;
char strbuf[LEN_OF_STR];
// stream is available after that
// reading numbers
fscanf(fp, "%d %d", &node, &edge);
// reading strings
for (int i = 0; i < node; i++) {
// reading line from input stream
fgets(strbuf, LEN_OF_STR, fp);
}
// cleaning input buffer
while (getchar() != '\n');
// reading lines with data
char str[NUM_OF_CHAR];
int a, b;
for (int i = 0; i < node; i++) {
// reading two numbers and two characters
fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]);
// do something with dada, e.g. output
printf("%d %d %c %c\n", a, b, str[0], str[1]);
}
return 0;
}
Когда вы читаете данные с scanf
или же fscanf
Вы можете проверить результаты, например:
if (4 == fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]))
{
// actions for correct data
}
else
{
// actions for wrong input
}
здесь строка формата имеет 4 спецификатора - "%d %d %c %c", поэтому мы проверяем как "сравнить возвращаемое значение с 4"
Я решил свою проблему. Я не должен использовать char*
указатель и сделать его указателем на массив. Первый параметр, который передается в fgets
функция должна быть char*
поэтому я должен был использовать только массив.
также, так как scanf
отсканировал уже с первой строки, если я использую fgets
Затем он автоматически получит следующую строку.
#include <stdio.h>
#define NUM_OF_CHAR 100
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char str[NUM_OF_CHAR] = {'\0'};
for (int i = 0; i < node; i++) {
fgets(str, NUM_OF_CHAR, fp);
}
printf("%s", str);
}