Анализ входных данных с помощью scanf на C

StackOverflow https://stackoverflow.com/questions/217074

  •  03-07-2019
  •  | 
  •  

Вопрос

У меня было много проблем, когда я пытался понять, как использовать scanf().Кажется, он прекрасно работает с целыми числами, будучи довольно прямолинейным scanf("%d", &i).

Где я сталкиваюсь с проблемами, так это при использовании scanf() в циклах пытается прочитать входные данные.Например:

do {
  printf("counter: %d: ", counter);
  scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
  1. Когда я ввожу корректно структурированный ввод, например c P101, кажется, он снова зацикливается, прежде чем вызвать меня.Кажется, это происходит даже с одним:

    scanf("%c", &c) 
    

    в цикле while.Он проделает цикл дважды, прежде чем снова выдаст мне запрос.Что заставляет его повторяться дважды, и как мне это остановить?

  2. Когда я ввожу меньший объем входных данных, который программно не содержал бы другого символа или числа, такого как q, нажатие клавиши enter, кажется, побуждает меня ввести больше.Как мне получить scanf() обрабатывать как одинарные, так и двухсимвольные записи?

Это было полезно?

Решение

Когда вы вводите " c P101 " программа фактически получает " c P101 \ n ". Большинство спецификаторов преобразования пропускают первые пробелы, включая переводы строк, но % c этого не делает. В первый раз все до & \ n " читается, второй раз вокруг " \ n " считывается в команду , " 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 () очень опасен (хотя и удобен для приложений без риска), поскольку изначально не выполняет никакой проверки ввода. Это ОЧЕНЬ широко известно как точка взлома, особенно атаки переполнения буфера, перезаписи пространства в регистрах, не выделенных для этой переменной. Поэтому, если вы решите использовать его, потратьте некоторое время на внесение каких-либо серьезных проверок / исправлений ошибок.

Надеюсь, это поможет кому-то в будущем, так как я предполагаю, что вы уже справились с этим! ;)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top