Анализ входных данных с помощью scanf на C
Вопрос
У меня было много проблем, когда я пытался понять, как использовать scanf()
.Кажется, он прекрасно работает с целыми числами, будучи довольно прямолинейным scanf("%d", &i)
.
Где я сталкиваюсь с проблемами, так это при использовании scanf()
в циклах пытается прочитать входные данные.Например:
do {
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
Когда я ввожу корректно структурированный ввод, например
c P101
, кажется, он снова зацикливается, прежде чем вызвать меня.Кажется, это происходит даже с одним:scanf("%c", &c)
в цикле while.Он проделает цикл дважды, прежде чем снова выдаст мне запрос.Что заставляет его повторяться дважды, и как мне это остановить?
Когда я ввожу меньший объем входных данных, который программно не содержал бы другого символа или числа, такого как
q
, нажатие клавиши enter, кажется, побуждает меня ввести больше.Как мне получитьscanf()
обрабатывать как одинарные, так и двухсимвольные записи?
Решение
Когда вы вводите " c P101
" программа фактически получает " c P101 \ n
". Большинство спецификаторов преобразования пропускают первые пробелы, включая переводы строк, но % c
этого не делает. В первый раз все до & команду
, " c
" считывается в префикс
и " P
" слева, который не является числом, поэтому преобразование завершается неудачно и " P101 \ n
" остается в потоке. В следующий раз " P
" сохраняется в команде " 1
" сохраняется в префиксе, а 1
(из оставшегося " 01
") сохраняется во входных данных с помощью " \ n
" все еще в потоке в следующий раз. Вы можете решить эту проблему, поставив пробел в начале строки формата, который пропустит все начальные пробелы, включая символы новой строки.
Похожая вещь происходит во втором случае, когда вы вводите « q
», « q \ n
». вводится в поток в первый раз вокруг " q
" читается, во второй раз " \ n
" читается, только при третьем вызове находится второй " q
" прочитайте, вы можете избежать этой проблемы снова, добавив пробел в начале строки формата.
Лучший способ сделать это - использовать что-то вроде fgets () для одновременной обработки строки, а затем использовать sscanf () для анализа.
Другие советы
Это действительно сломано! Я этого не знал
#include <stdio.h>
int main(void)
{
int counter = 1;
char command, prefix;
int input;
do
{
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
printf("---%c %c%d---\n", command, prefix, input);
counter++;
} while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
g3---
Вывод, похоже, соответствует ответу Роберта.
Как только у вас будет строка, содержащая эту строку.т. е."C P101", вы можете использовать возможности синтаксического анализа sscanf.
Видишь:http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html
По вопросу 1 я подозреваю, что у вас есть проблема с вашим printf ()
, так как нет завершающего символа " \ n ".
Поведение printf
по умолчанию заключается в буферизации вывода до тех пор, пока не будет заполнена полная строка. Это если вы явно не измените буферизацию в stdout
.
По вопросу 2 вы только что столкнулись с одной из самых больших проблем с scanf ()
. Если ваши данные не совпадают с указанной вами строкой сканирования, ваши результаты не будут соответствовать вашим ожиданиям.
Если у вас есть опция, вы получите лучшие результаты (и меньше проблем с безопасностью), игнорируя scanf ()
и проводя собственный анализ. Например, используйте fgets ()
, чтобы прочитать всю строку в строку, а затем обработать отдельные поля строки & # 8212; может быть, даже используя sscanf ()
.
Возможно, использование цикла while, а не цикла do ... while поможет. Таким образом, условие проверяется перед выполнением кода. Попробуйте следующий фрагмент кода:
while(command != 'q')
{
//statements
}
Кроме того, если вы знаете длину строки заранее, с циклами for можно работать намного проще, чем с циклами while. Есть также несколько хитрых способов динамического определения длины.
В качестве окончательного утверждения: scanf ()
не "отстой". Он делает то, что делает, и это все. fgets ()
очень опасен (хотя и удобен для приложений без риска), поскольку изначально не выполняет никакой проверки ввода. Это ОЧЕНЬ широко известно как точка взлома, особенно атаки переполнения буфера, перезаписи пространства в регистрах, не выделенных для этой переменной. Поэтому, если вы решите использовать его, потратьте некоторое время на внесение каких-либо серьезных проверок / исправлений ошибок.
Надеюсь, это поможет кому-то в будущем, так как я предполагаю, что вы уже справились с этим! ;) Р>