Две ваши самые большие проблемы - две наиболее распространенные проблемы, которые преследуют новых программистов на С с использованием scanf
:
- Вы используете неверную строку формата;и
- Вы не можете проверить возвращение
scanf
.
Давайте сначала разберемся с первым:
scanf("%[^\n]c", string);
Строка формата "%[^\n]c"
использует символьный класс спецификатор формата "%[...]"
, чтобы прочитать текст для string
.Затем следует "c"
- который будет совпадать только с литералом 'c'
в конце вашей входной строки.Это не может произойти, как написано, потому что "%[^\n]"
будет читать все символы, которые не являются '\n'
, оставляя только '\n'
для чтения - , который НЕ соответствует 'c'
.
Кроме того, спецификатор "%[...]"
вместе со спецификатором "%c"
НЕ занимают начальные пробелы ('\n'
- пробелы).Таким образом, оставив '\n'
непрочитанным в stdin
, ваш следующий вызов на scanf
не удастся, потому что "%[^\n]"
не будет читать '\n'
и не будет совпадать с 'c'
, что приведет к сбою совпадения , '\n'
остается непрочитанным в stdin
, и все быстро выходит из-под контроля.
Чтобы решить все проблемы, вам нужно запомнить (2.)
выше, а также использовать field-widthМодификатор для защиты границ массива string
, после чего вы должны прочитать и Сохранить символ, следующий за извлеченными, и вставить от string
до validate была прочитана полная строка ввода - и если нет, то ваша ответственность удалить все лишние символы, которые остаются в stdin
до попытка следующего чтения.
Для начала вы можете использовать правильно ограниченную строку формата , которая включает space
в начале, что приведет к отбрасыванию scanf
все пробелы, например
" %199[^\n]%c"
Примечание над финаломсимвол будет сохранен, два преобразования будут иметь место, поэтому вам понадобится символьная переменная для обработки результата окончательного спецификатора преобразования, например,
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
( примечание: приглашение было перемещено в цикле do {...} while (..);
)
Далее вы ответственны за проверку возврат из scanf
каждый раз .Вы должны обработать три условия
(return == EOF)
пользователь отменяет ввод, генерируя руководство EOF
, нажимая Ctrl + d (или в Windows Ctrl + z ); (return < expected No. of conversions)
, вы должны обработать соответствующий или input , и вы должны учитыватьдля каждого символа, который может остаться в вашем входном буфере.(обычно вы будете сканировать вперед во входном буфере до тех пор, пока не будет найден '\n'
или EOF
, отбрасывающий все оставшиеся посторонние символы, см. функцию empty_stdin()
в примере);и (return == expected No. of conversions)
, указывающий на успешное чтение - тогда вам нужно проверить, соответствует ли входные данные каким-либо дополнительным критериям (например, положительное целое число, положительная плавающая точка и т. д.).
В целом, вы можете обрабатывать чтение циклов с помощью scanf
и искать "quit"
в качестве ключевого слова, подсказывающего выход, следующим образом:
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
Наконец, НЕ используйте magic-номер в вашем коде (200
это магический номер ).Вместо этого, если вам нужна константа , #define
один (или больше).Единственное место, где вы должны жестко кодировать числа, это, например, модификатор scanf
field-width - который не может использовать переменную, макрос или именованную константу.Это одно исключение из правил.Аналогично, НЕ жестко кодируйте имена файлов или пути.Все функции принимают аргументы, даже main()
, передают необходимую информацию в вашу программу.
В целом, вы можете сделать что-то вроде:
#include <stdio.h>
#include <string.h>
#define MAXC 200 /* constant - maximum characters in string */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '\n')
c = getchar();
}
int main (void) {
char string[MAXC]; /* use constants for array bounds */
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
return 0;
}
Пример Использование / Вывод
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): My dog has fleas and my cat has none.
string: My dog has fleas and my cat has none.
Enter a bunch of words ('quit' exits): quit
string: quit
Создание руководства EOF
с помощью Ctrl + d (или Ctrl + z при повороте):
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): (user canceled input)
Сброс MAXC
до 20
и ширина поля модификатор до scanf
до 19
вы можете проверить обработку слишком длинных строк, например, первый вход подходит, второйслишком длинный:
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): my dog has fleas and my cat has none.
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): 1234567890123456789
string: 1234567890123456789
Enter a bunch of words ('quit' exits): 12345678901234567890
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): quit
string: quit
Посмотрите, что произошло, и дайте мне знать, если у вас есть дополнительные вопросы.