Почему scanf («% d», […]) не использует «\ n»? в то время как scanf («% c») делает?

ВотЯ видел это утверждение в принятом ответе:

Большинство спецификаторов преобразования пропускают первые пробелы, включая переводы строк, но%c не.

Для меня неясно, в чем причина такого различного поведения, я бы ожидал единого (например, всегда пропуск или никогда).

Я столкнулся с такой проблемой с куском кода на C:

#include "stdio.h"

int main(void){

    char ch;
    int actualNum;

    printf("Insert a number: ");
    scanf("%d", &actualNum);
    // getchar();

    printf("Insert a character: ");
    scanf("%c", &ch);

    return 0;
}

Обмен двухscanfs решает проблему, а также (прокомментировал)getcharв противном случае'\n' первой вставки будет потребляться второйscanf с%c, Я тестировал на gcc как на linux, так и на windows, поведение одинаковое:

GCC (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

Copyright (C) 2012 Free Software Foundation, Inc.

Это бесплатное программное обеспечение; см. источник для условий копирования. Там нет гарантии; даже не для ИЗДЕЛИИ или ФИТНЕСА ДЛЯ ОСОБЕННОЙ ЦЕЛИ.

Итак, мой вопрос: почему%d а также%c вести себя по-разному с.р.т.'\n' в ?scanf

 Cubbi07 нояб. 2012 г., 19:00
Вы могли бы использоватьscanf(" %c", &ch); (с пробелом перед%).
 Bo Persson07 нояб. 2012 г., 19:00
"%c" Формат используется для чтения одного символа, даже если это пробел. Как еще ты это сделаешь?
 TheBlastOne07 нояб. 2012 г., 19:01
 Alessandro S.08 нояб. 2012 г., 09:48
@RichardChambers: спасибо, вопрос не был четко сформулирован, я редактировал его, надеясь, что он станет более понятным.
 Richard Chambers07 нояб. 2012 г., 19:03
Не уверен, что ваш вопрос на самом деле. Пожалуйста, отредактируйте ваши сообщения, чтобы задать конкретный вопрос.
 RBerteig07 нояб. 2012 г., 20:09
Обратите внимание, что поведение зависит от режима буферизации, примененного кstdin, На терминале по умолчанию используется линейная буферизация. Фактически это означает, что входные данные предоставляются пакетами, как указано символом новой строки. Ни один призыв кscanf будет даже видеть любые символы, пока строка не заканчивается,но перевод строки включен во вход, Еслиstdin перенаправлен из файла, буферизация отличается, и поведение будет меняться.

Ответы на вопрос(4)

что пробел никогда не может быть целым числом, а пробел состоит из символов. Если вам нужен персонаж, попробуйте что-то вроде

scanf("%c", &ch );
while(isspace(c))
    scanf("%c" , &ch);

Или использовать!isalnum() если вы хотите разрешить только буквы и цифры или!isalpha() только для писем.

Решение Вопроса

лошадь»рот:

7.21.6.2 Функция fscanf...

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

...

7 Директива, которая является спецификацией преобразованияопределение катионанабор подходящих входных последовательностей, как описано ниже для каждого конкретногоэ. Спецификация конверсииКатион выполняется в следующие шаги:



8 Введите символы пробела (как указанопод редакциейisspace функция) пропускаются,если спецификациякатион включает в себя,[c, или жеn специфическаяэ.284)



9 Элемент ввода читается из потока, если не указанокатион включаетn специфическаяэ.Элемент ввода определенned как самая длинная последовательность вводимых символов, которая не превышает какого-либо указанногоредширина поля и которая является или является предварительнымх, соответствующая входная последовательность. 285) ФИПервый символ, если он есть, после элемента ввода остается непрочитанным. Если длина входного элемента равна нулю, выполнение директивы заканчивается неудачей; это условие не соответствует, если конецle, ошибка кодирования или ошибка чтения препятствовали вводу из потока, и в этом случае это сбой ввода.284) Эти пробельные символы не учитываютсяредширина поля.

285) fscanf возвращает не более одного входного символа во входной поток. Поэтому некоторые последовательности, которые приемлемы для strtod, strtol и т. Д., Неприемлемы для fscanf.

Акцент добавлен мной.

Пробел не является частью допустимой целочисленной строки, поэтому имеет смысл%d спецификатор преобразования, чтобы пропустить любой начальный пробел. Тем не менее, пробелы могут быть действительными сами по себе, так что это имеет смысл для%c спецификатор преобразования вне пропустить это.

В соответствии с пунктом 5 выше, если вы поместите пробел в строку формата до%c директива, все ведущие пробелы будут пропущены:

scanf(" %c", &ch);
 Alessandro S.08 нояб. 2012 г., 09:54
Благодаря @Джон Боде, я использую язык Си несколько раз в год, и я склонен забывать много деталей, которые я знал, чрезвычайно важно иметь такой документ для ссылки на подобные проблемы.

)

scanf("%c", some_char); // reads a character from the key board.
scanf("%d", some_int);  // reads an integer from the key board.

Так что, если я сделаю это:

printf("Insert a character: ");
scanf("%c", &ch);                // if I enter 'f'. Really I entered 'f' + '\n'
                                 // scanf read the first char and left the '\n'
printf("Insert a number: ");
scanf("%d", &actualNum);      // Now scan if is looking for an int, it sees the '\n'
                              // still, but that's not an int so it waits for the 
                              // the next input from stdin

Это'разве это не так?s потребляет новую строку самостоятельно в этом случае. Попробуйте это вместо этого:

char ch;
char ch2;
printf("Insert a character: ");
scanf("%c", &ch);
printf("Insert another character: ");
scanf("%c", &ch2); 

Будет "пропускать" второйscanf() потому что это читается в'\n' в это время.scanf() является последовательным, и вы ДОЛЖНЫ потреблять это'\n' если ты'собирается использовать это правильно.

 Mike08 нояб. 2012 г., 14:26
@Alessandro - рад помочь
 Alessandro S.08 нояб. 2012 г., 09:51
Большое спасибо, даже если ваши ответы были одинаково понятнылошадь»ротик " из принятого был дополнительной ценностью для меня!
 M.M12 нояб. 2014 г., 04:34
Эта логика нет действительно работает; еслиscanf("%d" видит письмо, то оно неПропустить это и ждать, пока Int.
 Mike12 нояб. 2014 г., 18:22
@Matt Да, вы правы. Я пытался упростить ответ для OP, и я думаю, что я упростил его, я немного изменил его, чтобы убрать ошибку ... но мне все еще нужно придумать лучшую формулировку

Я не эксперт, но ямы обнаружили, что делаем:

fflush(stdin);

после каждого сканирования помогает ...

Такие как:

scanf("%c", &ch);
fflush(stdin);
 Draconian Times09 нояб. 2012 г., 11:44
Хорошо, я понимаю. fflush, используемый таким образом, имеет неопределенное поведение.
 John Bode07 нояб. 2012 г., 19:45
Поведениеfflush не определен для входных потоков; то, что она делает для какой-либо конкретной системы, не гарантирует ее правильности или повторяемости. Не использоватьfflush очистить поток ввода.

Ваш ответ на вопрос