Подробное объяснение scanf на языке C, причина постоянной ошибки сканирования: в прошлый раз буфер содержал мусорный ввод!

Оглавление

Имя функции: сканирование

(A) Спецификатор формата

(B) Пробелы

(C) Символы без пробелов

 Использование управляющих строк функции scanf()

Когда функция scanf() получает входные данные, она завершает ввод данных в следующих ситуациях:

Вопрос 2. Функция scanf() не может корректно принимать строки с пробелами? Например: Я люблю тебя!

Вопрос 3: Проблема с остаточной информацией в буфере клавиатуры

Вопрос 4. Как бороться с тупиковой ситуацией в программе или ошибками, вызванными неправильным вводом в функцию scanf()?

Обсуждение функции сканирования

1. Проблема с пустыми символами

2. Проблема с буфером

 


Имя функции: сканирование

Функция: Выполнение форматированного ввода
Использование: int scanf(char *format[,argument,...]);
Функция scanf() — это общая функция терминального форматированного ввода. Она считывает входную информацию со стандартного устройства ввода (клавиатуры). Может считывать данные любого встроенного типа и автоматически преобразовывать значения в соответствующий встроенный формат.
Формат вызова: scanf("<форматированная строка>",<таблица адресов>);
Функция scanf() возвращает количество успешно назначенных элементов данных и возвращает EOF при возникновении ошибки.
Его управляющая строка состоит из символов трех типов:
1. Спецификатор формата;
2. Символ пробела;
3. символы без пробелов;

(A) Спецификатор формата

Форматирование символов иллюстрировать Форматирование символов иллюстрировать
Чтение значения с плавающей запятой (действительно только в C99) %f Чтение числа с плавающей запятой
То же, что и выше Чтение числа с плавающей запятой
прочитать персонажа Чтение числа с плавающей запятой
Прочитать десятичное целое число Чтение числа с плавающей запятой
Чтение десятичных, восьмеричных и шестнадцатеричных целых чисел. Чтение числа с плавающей запятой
Чтение восьмеричного целого числа %п читать в указателе
%Икс Чтение шестнадцатеричного целого числа Чтение беззнакового десятичного целого числа.
%ИКС То же, что и выше %n Эквивалентное количество символов значения, которое было прочитано на данный момент.
прочитать персонажа %[ ] Сканировать набор символов
%s Читать в строке %% Прочитать знак %
Дополнительная таблица символов описания формата
модификатор иллюстрировать
л/л модификатор длины Введите «длинные» данные
час модификатор длины Введите «короткие» данные
Вт целочисленная константа Укажите ширину входных данных
* (Звездочка) Чтение пустых данных hh, ll аналогичны h, l, но действительны только для C99.

 

(B) Пробелы

Пустые символы заставляют функцию scanf() пропускать один или несколько пустых символов во входных данных во время операции чтения . Пустыми символами могут быть пробел, табуляция, новая строка и т. д., пока не появится первый непустой символ.

(C) Символы без пробелов

Символ без пробелов заставляет функцию scanf() удалять при чтении символы, идентичные символу без пробелов.


 Использование управляющих строк функции scanf()


Пример 1.

#include "stdio.h"
int main(void)
{
int a,b,c;

scanf("%d%d%d",&a,&b,&c);
printf("%d,%d,%d/n",a,b,c);
return 0;
}

=======================================
运行时按如下方式输入三个值:
3□4□5 ↙(输入a,b,c的值)
3,4,5 (printf输出的a,b,c的值)

=======================================
以下是合法输入方式:
① 3□□4□□□□5↙
② 3↙
4□5↙
③ 3(tab键)4↙
5↙

(1) & в &a, &b, &c — это оператор адреса, который получает адреса памяти этих трех переменных соответственно.
(2) «%d%d%d» означает ввод трех значений в десятичном формате. При вводе два данных могут быть разделены одним или несколькими пробелами, клавишей табуляции или клавишей ввода.

Пример 2.


#include "stdio.h"
int main(void)
{
int a,b,c;
scanf("%d,%d,%d",&a,&b,&c);
printf("%d,%d,%d/n",a,b,c);
return 0;
}

============================================================

运行时按如下方式输入三个值:
3,4,5 ↙(输入a,b,c的值)
或者
3,□4,□5 ↙(输入a,b,c的值)
3,□□□4,□5 ↙(输入a,b,c的值)
......
都是合法的,但是","一定要跟在数字后面,如:
3□,4,□5 ↙就非法了,程序出错。(解决方法与原因后面讲)

============================================================

再如:
1、sacnf()中的变量必须使用地址。
int a, b;
scanf("%d%d",a,b); //错误
scanf("%d%d",&a,&b);

============================================================

2、scanf()的格式控制串可以使用其它非空白字符,但在输入时必须输入这些字符。
例:
scanf("%d,%d",&a,&b);
输入: 3,4 ↙(逗号与"%d,%d"中的逗号对应)
scanf("a=%d,b=%d",&a,&b);
输入: a=3,b=4 ↙("a=","b=",逗号与"%d,%d"中的"a=","b="及逗号对应)

============================================================

3、在用"%c"输入时,空格和“转义字符”均作为有效字符。
例:
scanf("%c%c%c",&c1,&c2,&c3);
输入:a□b□c↙
结果:a→c1,□→c2,b→c3 (其余被丢弃)


1. Переменные в sacnf() должны использовать адреса.

2. Строка управления форматом функции scanf() может использовать и другие непустые символы, но эти символы необходимо вводить при вводе.

3. При вводе с помощью «%c» допустимыми символами являются пробелы и «escape-символы».
 

Когда функция scanf() получает входные данные, она прекращает ввод данных в следующих ситуациях :

(Вместо завершения функции scanf функция scanf содержит данные только в каждом поле данных и завершается после нажатия Enter).
① При появлении пробела нажмите клавиши «Enter» или «Tab».
② Конец при достижении ширины.
③ В случае незаконного ввода.

Вопрос 2. Функция scanf() не может корректно принимать строки с пробелами? Например: Я люблю тебя!

#include <stdio.h>
int main()
{
    char str[80];

    scanf("%s",str);
    printf("%s",str);
    return 0;
}
========================
输入:I live you!
输出:I

Поэтому приведенная выше программа не может достичь ожидаемой цели . Когда scanf() сканирует пространство после «I», она считает присвоение str завершенным и игнорирует следующее «love you!» . Здесь следует отметить, что « люблю тебя!» все еще есть. Буфер клавиатуры (по этому вопросу я видел в Интернете именно это. Однако после отладки я обнаружил, что на самом деле первый и последний указатели строки буфера уже равны в этом время, что означает, что буфер опустошается.Функция scanf() Она должна просто сканировать поток stdin, остаточная информация находится в stdin ). Давайте изменим приведенную выше программу, чтобы проверить:

#include <stdio.h>
#include <unistd.h> 
//引入sleep()函数说明:sleep()会令目前的进程暂停, 直到达到参数seconds 所指定的时间, 或是被信号所中断.
int main()
{
    char str[80];
    char str1[80];
    char str2[80];

    scanf("%s",str);/*此处输入:I love you! */
    printf("%s",str);

    sleep(2);/*这里等待2秒,告诉你程序运行到什么地方*/

    scanf("%s",str1);/*这两句无需你再输入,是对键盘盘缓冲区再扫描 */
    scanf("%s",str2);/*这两句无需你再输入,是对键盘盘缓冲区再扫描 */

    printf("\n%s",str1);
    printf("\n%s",str2);

    return 0; 
}
============================================
输入:I love you!
输出:I
love
you!


Хорошо, теперь, когда мы знаем причину, сможет ли функция scanf() выполнить эту задачу?
Ответ: да! Не забывайте, что функция scanf() также имеет управляющий символ формата %[ ] . См. следующую программу:

%[ ] Сканировать набор символов. Квадратные скобки указывают набор символов, который необходимо сканировать.
%[^ ] Соответствует всем символам, кроме символов [...], например, [^aeiou] соответствует всем буквам в строке «google runoob taobao», кроме букв eoua.

Содержимое здесь немного похоже на правила регулярных выражений:

#include "stdio.h"
int main()
{
char string[50];


    /*scanf("%s",string);不能接收空格符*/

    scanf("%[^\n]",string);  //^\n表示:读入输入直到遇到 \n截止

    printf("%s\n",string);

    return 0; 

}

Дополнение: Редкий, но полезный символ преобразования в scanf: [...] и [^...].

#include <stdio.h>
int main()
{
    char strings[100];
    scanf( "%[1234567890] ",strings);
    printf( "%s",strings);
    return 0;
}
===========================
$ ./main
123456asdf1231
123456Program exited with status 0

После запуска и ввода: 1234werew1234 результат: 1234.
Запустив его, можно обнаружить, что его функция такова: если входной символ принадлежит символу в строке в квадратных скобках, то извлечь символ ; если обнаружено, что он не принадлежит, извлечение заканчивается . Этот метод автоматически добавит признак конца строки в конец извлеченных символов. scanf( "%[^1234567890] ",strings); Его функция: если обнаружено, что введенный символ принадлежит символу в строке в квадратных скобках, то извлечение закончится; если он не принадлежит, персонаж будет извлечен. Этот метод автоматически добавит признак конца строки в конец извлеченных символов.
Примечание. С обеих сторон квадратных скобок не может быть пробелов, например: scanf( "%[ 1234567890 ] ",strings); scanf( "%[ ^1234567890] ",strings); Даже если пробелы не допускаются, будет включено.
Этот метод также может решить проблему, заключающуюся в том, что во входных данных scanf не может быть пробелов. Просто используйте scanf( "%[^\n] ",strings); и все. Это потрясающе.


Вопрос 3: Проблема с остаточной информацией в буфере клавиатуры

#include <stdio.h>
int main()
{
    int a;
    char c;
    do
	{
	    scanf("%d",&a);
	    scanf("%c",&c);
	    printf("a=%d c=%c\n",a,c);
	    printf("c=%d\n",c);//将字符输出成十进制数字,查验c里面现在到底是什么?
	}while(c!='N');
}
==============================

8             //任意输入一个整型数据,比如8之后按了回车。
a=8 c=        //这行是之后的输出结果

c=10      //根据ASCII表 十进制数10 表示换行符 也就是空格,证明第二行的scanf输入了第一行结尾的回车
          //由于循环的原因,若不是=‘N’就会无限让你输入

N
a=8 c=N
c=78

--------------------------------


scanf("%c",&c); Это предложение не может нормально принимать символы. В чем причина?

Мы используем printf("c=%d\n",c); чтобы выразить C как int, включите printf("c=%d\n",c); и посмотрим, как функция scanf() назначается C. Что именно так, результат c=10, каково значение ASCII, равное 10? Разрыв строки — /n. Кстати, каждый раз, когда мы нажимаем клавишу «Ввод», в буфер клавиатуры отправляются «возврат каретки» (\r) и «перевод строки» (\n). Здесь \r — это заменено на функцию scanf(), обработало его (подумаем так^_^) , а \n было «неправильно» присвоено c функцией scanf() .


Решение: Вы можете добавить fflush(stdin); после двух функций scanf(), и getch(); getchar(); также возможно, но это зависит от конкретного оператора scanf(), который здесь не будет анализироваться. читатели могут исследовать сами. Но добавление fflush(stdin); работает независимо от ситуации.
Имя функции: fflush
Функция: Очистить поток
Использование: int fflush(FILEstream );
 

#include <stdio.h>
int main()
{
	int a;
	char c;
	do
	{
		scanf("%d",&a);
		fflush(stdin);   
		scanf("%c",&c);
		fflush(stdin);
		printf("a=%d c=%c\n",a,c);
	}while(c!='N');
}

Вот еще один пример использования «пробела» для обработки остаточной информации в буфере:

#include <stdio.h>
int main()
{
	int i;
	char j;
	for(i = 0;i < 10;i++)
	{
		scanf("%c",&j);/*这里%前没有空格*/
		printf("%c",j);
	}
}
===============================
这里的输出会把空格当做字符信息
i love you    //输入
i love you    //输出



使用了空格控制符后:
#include <stdio.h>
int main()
{
	int i;
	char j;
	for(i = 0;i < 10;i++)
	{
		scanf(" %c",&j);/*注意这里%前有个空格*/
		printf("%c",j);
	}
}
===========================
这里的输出会把空格自动略去
i love you    //输入
iloveyou      //输出


Вы можете запустить его, чтобы увидеть разницу между двумя программами.


Вопрос 4. Как бороться с тупиковой ситуацией в программе или ошибками, вызванными неправильным вводом в функцию scanf()?

 

#include <stdio.h>
int main()
{
    int a,b,c; /计算a+b/
    scanf("%d,%d",&a,&b);
    c=a+b;
    printf("%d+%d=%d",a,b,c);
}


В приведенной выше программе, если значения a и b введены правильно, проблем не возникнет. Однако вы не можете гарантировать, что пользователь сможет каждый раз вводить их правильно. Если введен неправильный тип, ваша программа либо произойдет тупик, либо будет получен неправильный результат. Ха-ха, это может быть проблема, с которой сталкивался каждый, верно?
Решение: Когда функция scanf() выполняется успешно, возвращаемое значение представляет собой количество успешно прочитанных переменных. Другими словами, ваша функция scanf() имеет несколько переменных . Если функция scanf() нормально считывает все переменные, она будет вернуть количество переменных, которые он имеет. . Но здесь следует отметить еще одну проблему: если введены недопустимые данные, в буфере клавиатуры может остаться остаточная информация .
Правильный распорядок дня:
 

#include <stdio.h>
int main()
{
    int a,b,c; /计算a+b*/
    while(scanf("%d,%d",&a,&b)!=2)    fflush(stdin);
    c=a+b;
    printf("%d+%d=%d",a,b,c);
}

Обсуждение функции сканирования

1. Проблема с пустыми символами

#include <stdio.h>
main()
{
    int a;
    printf( "input the data/n ");
    scanf( "%d\n ",&a);//这里多了一个回车符/n
    printf( "%d ",a);
    return 0;
}


В результате программа завершается вводом двух чисел вместо ожидаемого. почему?
Причина: если заканчивается пробельным символом, scanf пропустит этот пробел и прочитает следующий символ, поэтому вам придется ввести другое число. Пробелы здесь включают пробелы, табуляцию, переводы строк, возвраты каретки и переводы страниц. Поэтому, если вы используете scanf( "%d ",&a), возникнет та же проблема.
Решение: ошибки такого рода чаще всего возникают из-за невнимательности при наборе текста, просто будьте внимательнее. Такую проблему нелегко проверить, при компиляции проблем нет.

Нелегко увидеть даже одно пространство. Если в вашей программе возникают вышеперечисленные проблемы, просто проверьте это самостоятельно.

2. Проблема с буфером

Эту ошибку очень легко совершить , и я много раз ее пропускал.

#include <stdio.h>
main()
{
    int n = 5;
    char c[n];
    for(int i = 0; i < n; i++)
        c[i] = scanf( "%c ",&c[i]);
    printf(c);
    return 0;
}
======================
asdfghjk

--------------------------------


Если вы введете:
a
b
c
, то цикл завершится «раньше».
Причина: после ввода a и первого возврата каретки a и возврат каретки остаются в буфере. Первое сканирование читает a, но во входном буфере все еще есть /n, а второе сканирование читает это /n. Затем введите b и второй возврат каретки. Аналогично, третье сканирование читает b, а четвертое сканирование читает второй возврат каретки. Пятое чтение c. Таким образом, все пять сканирований были выполнены и не завершились досрочно. Просто некоторые сканы читают символ возврата каретки.

解决方法:把程序改成这样就可以了:
for( i = 0; i < n; i++){
    scanf( "%c ",&c[i]);
    fflush(stdin);//刷新缓冲区
}

或者不用scanf,而用gets()函数,如:
#include <stdio.h>
main()
{
    char c[5];
    gets(c);
    printf(c);
    return 0;
}


Но обратите внимание: эта функция автоматически преобразует последний введенный вами возврат каретки в символ «/0». Если ваш ввод превышает размер массива, произойдет ошибка.

 

 

Прототип scanf: см. «Энциклопедию языка C» и K&C.

 

Supongo que te gusta

Origin blog.csdn.net/m0_46299442/article/details/116072365
Recomendado
Clasificación