Разбор ввода с помощью scanf в C - PullRequest
10 голосов
/ 20 октября 2008

У меня было много проблем, пытаясь понять, как использовать 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, нажатие клавиши ввода, похоже, побуждает меня вводить больше. Как мне получить scanf() для обработки как одиночных, так и двойных символов?

Ответы [ 5 ]

20 голосов
/ 20 октября 2008

Когда вы вводите "c P101", программа фактически получает "c P101\n". Большинство спецификаторов преобразования пропускают первые пробелы, включая переводы строк, но %c этого не делает. Первый раз, когда все до тех пор, пока не будет прочитано "\n", второй раз, когда "\ n" будет прочитано в command, "c" прочитано в prefix и "P" слева, который не является числом, поэтому преобразование завершается неудачно, и "P101\n" остается в потоке. В следующий раз, когда «P» будет сохранено в команде, «1» будет сохранено в префиксе, а 1 (из оставшихся «01») будет сохранено на входе, а «\n» все еще включено поток в следующий раз. Вы можете решить эту проблему, поставив пробел в начале строки формата, который пропустит все начальные пробелы, включая символы новой строки.

Аналогичная вещь происходит во втором случае, когда вы вводите "q", "q\n" вводится в поток, первый раз, когда читается "q", второй раз, когда «\n» читается, только при третьем вызове происходит второе чтение «q», вы можете избежать этой проблемы снова, добавив пробел в начале строки формата.

Лучший способ сделать это - использовать что-то вроде fgets () для одновременной обработки строки, а затем использовать sscanf () для анализа.

1 голос
/ 20 октября 2008

Как только у вас есть строка, содержащая строку. то есть "C P101", вы можете использовать возможности синтаксического анализа sscanf.

См: http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html

1 голос
/ 20 октября 2008

Это действительно сломано! Я этого не знал

#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---

Вывод, кажется, соответствует ответу Роберта.

1 голос
/ 20 октября 2008

Для вопроса 1, я подозреваю, что у вас проблема с вашим printf(), так как нет завершающего "\ n".

Поведение по умолчанию printf заключается в буферизации вывода до тех пор, пока он не завершит полную строку. Это если вы явно не измените буферизацию на stdout.

В вопросе 2 вы только что столкнулись с одной из самых больших проблем с scanf(). Если ваши входные данные не будут точно соответствовать указанной вами строке сканирования, ваши результаты не будут соответствовать вашим ожиданиям.

Если у вас есть опция, вы получите лучшие результаты (и меньше проблем с безопасностью), проигнорировав scanf() и выполнив собственный анализ. Например, используйте fgets(), чтобы прочитать всю строку в строку, а затем обработать отдельные поля строки & mdash; возможно даже используя sscanf().

0 голосов
/ 20 ноября 2012

Возможно использование цикла while, а не цикла do ... while поможет. Таким образом, условие проверяется перед выполнением кода. Попробуйте следующий фрагмент кода:

 while(command != 'q')
    {
        //statements
    }

Кроме того, если вы знаете длину строки заранее, с циклами for можно работать намного проще, чем с циклами while. Есть также несколько хитрых способов динамического определения длины.

В качестве последней напыщенной речи: scanf() не "отстой". Он делает то, что делает, и это все. fgets() очень опасно (хотя и удобно для приложений без риска), поскольку изначально не выполняет никакой проверки ввода. Это ОЧЕНЬ широко известно как точка взлома, особенно атаки переполнения буфера, перезаписи пространства в регистрах, не выделенных для этой переменной. Поэтому, если вы решите использовать его, потратьте некоторое время на исправление ошибок.

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

...