2008-10-19 18 views
10

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

Где я сталкиваюсь с проблемами, используется 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() для обработки как одиночных, так и двойных символов?

ответ

20

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

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

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

+2

Очень хорошее объяснение, я подозревал, что он должен читать \ n, но это, безусловно, усложняло то, что я считал простым анализом. Я посмотрю на fgets и sscanf(). – zxcv

+0

Не могли бы вы добавить абзац о проверке возвращаемого значения из 'scanf()', чтобы убедиться, что вы получили то, что ожидали. Если вы не знаете более раннего вопроса, это, скорее всего, станет каноническим Q & A для этой проблемы, но канонический ответ должен охватывать все разумные моменты, и я думаю, что возвращение из 'scanf()' является разумной точкой.Сделайте комментарий, направленный на меня, когда вы это сделаете; Затем я удалю этот комментарий и поставлю комментарий для удаления (или вы можете отметить это как устаревшее, но я хотел бы знать, что вы внесли изменения). Благодарю. –

1

На вопросе 1, я подозреваю, что у вас есть проблемы с вашим printf(), так как нет завершающего «\ п».

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

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

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

+0

Хм, я не думаю, что это так. даже при вставке \ n, он просто распечатает его дважды, прежде чем scanf снова предложит мне. Я даже попытался поместить printf до и после scanf (внутри цикла do-while), и они повторяются дважды, прежде чем scanf снова запустится. Это с одним символьным входом, например s и return. – zxcv

-1

scanf отстой, по причинам, которые вы уже обнаружили. Гораздо лучше использовать что-то, чтобы получить всю строку, а затем проанализировать ее.

+0

Я открыт для предложений ... что вы предлагаете - это лучшая альтернатива? – zxcv

+0

Использовать что? Вы говорите, что что-то отстой, но не предоставляют альтернативы? – Alfred

+0

fgets - самое простое решение. Но есть библиотеки C, которые делают это безопасно. –

1

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

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

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

0

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

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

Кроме того, если вы знаете длину строки загодя, «для» петли может быть гораздо проще работать, чем «а» петли. Существуют также более сложные способы динамического определения длины.

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

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

+0

Почему вы говорите, что «fgets» опасен? Может быть, вы путаете его с 'get'? – interjay

+0

моя ошибка, я имел в виду «получает()», поздно ночью кодирование! ... – jap3r

Смежные вопросы