Skip to content

LV010-输入简介

这里主要是一些输入函数的说明。

一、getchar

1. 函数说明

在 linux 下可以使用 man 3 getchar 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int getchar(void);

函数说明】 C 标准库 <stdio.h> 中的函数,从标准输入 stdin 获取一个字符,比如我们从键盘输入字符 A , B , C ,并按下回车后,我们的输入被放入了输入缓冲区,这个时候 getchar() 会从缓冲区中读取我们刚才的输入,一次只读取一个字符。

函数参数】 none

使用格式】 none

返回值】 int 类型,该函数以无符号 char 强制转换为 int 的形式 返回读取的字符,如果读取结束或发生读错误,则返回 EOF(-1) (输入 Ctrl+d ,就会返回 EOF )。

注意事项

(1)通过该函数输入的都是字符,例如输入数字 1 ,实际输入的是字符 1 。

(2)以 Enter 结束输入(空格不会导致输入结束结束),他可以接受空格字符, Enter 结束输入时,接受空格,会舍弃最后的回车符。

2. 使用实例

c
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
	char a;
	char b;
	printf("请输入字符:");
	a = getchar();
	b = sizeof(a);
	printf("输入的字符:");
	putchar(a);
	printf("\n");
	printf("a = %d, a = %x, b = %d\n",a, a, b);
	return 0;
}

运行结果如下:

image-20260206141447192

二、gets

1. 函数说明

在 linux 下可以使用 man 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
char *gets(char *s);

函数说明】 C 标准库 <stdio.h> 中的函数,从标准输入 stdin 读取一行(以 <Enter> 结束),并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

函数参数

  • str : char * 类型,这是指向一个字符数组的指针,该数组存储了 C 字符串。

使用格式】 none

返回值】 char * 类型,如果成功,该函数返回 str 。如果发生错误或者到缓冲区末尾时还未读取任何字符,则返回 NULL 。

注意事项

(1)使用该函数的时候会产生警告,这是因为在后来的标准中已经移除该函数,取而代之的是 fgets 。

(2) gets 从键盘输入一以 回车结束 的字符串放入 字符数组 中,并自动加 \0 ,需要注意的是输入字符串长度应小于字符数组维数,遇到回车时结束读取。

(2)与 scanf 函数不同, gets 函数并不以 空格 作为字符串输入结束的标志,它可以读取空格,遇到空格不会结束读取。

(3)可以读取 Tab 键,但是我测试的时候貌似把 tab 当做一个空格进行处理了。

2. 使用实例

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	char str[50];
	printf("请输入字符串:");
	gets(str);
	printf("输入的字符串:");
	puts(str);
	printf("str[50] = %s\n", str);
	return 0;
}

运行结果如下:

image-20260206141731750

三、scanf

1. 函数说明

在 linux 下可以使用 man 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int scanf(const char *format, ...);

函数说明】 C 标准库 <stdio.h> 中的函数,从标准输入 stdin 读取格式化输入,它不会读取空格、制表符、回车符,这些符号将会留在缓冲区中。

函数参数

  • format : char * 类型,格式化字符串( format ),这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符 和 format 说明符。format 说明符形式为
md
%[*][width][modifiers]type

参数含义如下:

参数 描述
* 这是一个可选的星号,表示数据是从流 stream 中读取的,但是可以被忽视,即它不存储在对应的参数中。
width 这指定了在当前读取操作中读取的最大字符数。
modifiers 为对应的附加参数所指向的数据指定一个不同于整型(针对 d、i 和 n)、无符号整型(针对 o、u 和 x)或浮点型(针对 e、f 和 g)的大小。
type 一个字符,指定了要被读取的数据类型以及数据读取方式。具体参见下一个表格。
  • ... :附加参数(地址表),根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。

使用格式

c
scanf("<格式化字符串>", <地址表>);

返回值】 int 类型,如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF(-1) 。

注意事项

(1) scanf 读取结束的标志:

  • < space > 、 Tab 、或 Enter 。
  • 遇非法输入。
  • 遇宽度结束(就是输入的时候有类似 %4d ,这种的话,要是我们输入 212345 ,那么它只会读取 4 个字符)。

(2)连续输入字符型变量(即使用 %c )的时候,空格和转义字符会作为有效字符输入(后边会有详细的实例说明)。

c
scanf(“%c%c%c”,&c1, &c2, &c3);
若输入a b c   
则c1为a, c2为空格, c3为b

2 格式化字符串详解

2.1 *

基本没有用过,这里不再说明,以后用到了再补充。

2.2 格式字符( type )

格式字符 形式 意义
i, d %i, %d 输入或读取十进制整数,数字前面的 + 或 - 号是可选的。
u %u 输入或读取十进制无符号整数。
o %o 输入或读取八进制无符号整数。
x, X %x, %X 输入或读取十六进制无符号整数。
f, F %f, %F 输入或读取小数形式(指数形式浮点小数)浮点数。
e, E %e, %E
g, G %g, %G
c %c 输入或读取单个字符。空格和转义字符作为有效字符输入。
s %s 输入或读取字符串,这将读取连续字符,直到遇到一个空格字符(空格字符可以是空白、换行和制表符)。

注意事项

(1)输入 double 类型数据必须用 lf ,不能用 f 。

(2)输入 % ,可以用 %% 。

2.3 数据宽度( width )

参数 描述
number 指定输入数据宽度,遇空格或不可转换字符结束。

2.4 修饰符( modifiers )

参数 描述
h 用于 d, o, x 前,指定输入为 short 型整数。
l 用于 d, o, x 前,指定输入为 long 型整数。
用于 e, f 前,指定输入为 double 型实数。
* 抑制符,指定输入项读入后不赋给变量。

3. 地址表详解

scanf 函数读取时,后边的地址表代表的是地址,也就是说,必须是地址的形式。

类型 说明
变量 对于变量,应该加上 & 符号,如:&a, &b 等。
数组 对于数组,数组名就代表了该数组的起始地址,所以不需要加 & 。如:a [12]→ a 即可。
指针 对于指针,指针名就代表了该指针指向的地址,所以不需要加 & 。如: *p → p 即可。

4. 使用实例

4.1 一般使用实例

  • 格式字符串必须严格按书写格式输入
c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int a, a1, a2;
	int b, b1, b2;
	int c, c1, c2;
	printf("请输入数据:");
	scanf("%d%d%d",&a, &b, &c);
	printf("输入的数据为:a = %d, b = %d, c = %d\n", a, b, c);

	printf("请输入数据:");
	scanf("%d,%d,%d",&a1, &b1, &c1);
	printf("输入的数据为:a1 = %d, b1 = %d, c1 = %d\n", a1, b1, c1);

	printf("请输入数据:");
	scanf("%d %d %d",&a2, &b2, &c2);
	printf("输入的数据为:a2 = %d, b2 = %d, c2 = %d\n", a2, b2, c2);
	
	return 0;
}

运行效果:

image-20260206142206954

注意事项

(1) scanf 中的格式字符串中除了 % 部分的格式字符,其他的自行添加的(如 , 、其他的字母等)在输入的时候要严格按照格式一起输入,否则输入的数据会有问题。经实验证明,格式字符串中的空格(并不是从终端输入的时候的字符串)似乎不会对输入结果产生影响。

(2)在同时进行多个数字变量的数据输入时,相邻数据之间可以用 < space > 、 < Tab > 或者 < Enter > 均可,但要注意自己是否添加了其他字符。

4.2 字符输入时的空格

4.2.1 测试源码
c
#include <stdio.h>
int main(int argc, char *argv[])
{
	char a, b, c, d;
	printf("请输入字符(a b c):");
	scanf("%c%c%c",&a, &b, &c);
	printf("输入的字符为:a = %c(%#x), b = %c(%#x), c = %c(%#x)\n", a, a, b, b, c, c);

	printf("请输入字符(d):");
	scanf("%c",&d);
	printf("输入的字符为:d = %c(%#x)\n", d, d);

	return 0;
}

注意事项

(1)在用 %c 进行单个字符输入时,空格和转义字符作为有效字符输入

(2)在测试过程中,若有连续两个 scanf 函数进行输入时,上一个 scanf 多余出来的数据(包括最后的 < Enter > )将会直接作为下一个 scanf 的输入。

4.2.2 连续输入测试

我们这样输入数据:

c
请输入字符(a b c):123

然后我们就会得到这样的输出数据:

image-20260206142951903

我们查阅一下 ASCII 码表就会发现,0xa 其实就是表示 \n ,也就是换行。上边的现象就说明,换行符号也被当做一个字符,并且直接被当做下一个 scanf 的输入了。

4.2.3 中间带空格
c
请输入字符(a b c):1 23

我们会得到以下输出信息:

image-20260206142633472

可以看到,空格被赋给了 b,最后的 3 则直接赋给了 d。

4.2.4 中间带回车

我们输入完 12 后,直接输入一个回车,根据前边的现象,a 会被赋值为 1,b 会被赋值为 2,c 会被赋值为 0xa,然后由于缓冲区数据了,所以当遇到第二个 scanf 的时候会提示我们输入字符:

c
请输入字符(a b c):12

我们会看到这样的输出:

image-20260206142828881

4.3 字符串输入时的空格

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	char str1[20];
	char str2[20];
	printf("请输入字符串:");
	scanf("%s",str1);
	printf("输入的字符串为:str[20] = %s\n", str1);

	printf("请输入字符串:");
	scanf("%s",str2);
	printf("输入的字符串为:str[20] = %s\n", str2);

	return 0;
}

image-20260206143306992

注意事项

(1) %s 进行字符串输入时,遇到 < space > 即认为该数据结束。

(2)若有连续两个 scanf 函数进行输入时,上一个 scanf 输入的数据若有 < space > ,则 < space > 后的数据,将会直接作为下一个 scanf 的输入。

4.4 缓冲区的多余字符

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int y,m,d;
	int y1,m1,d1;
	int a;
	char b;
	float c;
	
	printf("请输入数据:");
	scanf("%4d%2d%2d", &y, &m, &d);
	printf("输入的数据为:y = %d, m = %d, d = %d\n", y, m, d);

	printf("请输入数据:");
	scanf("%4d%3d%2d", &y1, &m1, &d1);
	printf("输入的数据为:y1 = %d, m1 = %d, d1 = %d\n", y1, m1, d1);

	printf("请输入数据:");
	scanf("%d%c%f",&a,&b,&c);
	printf("输入的数据为:a = %d, b = %c, c = %f\n", a, b, c);
	return 0;
}

我们这样输入数据,

c
请输入数据:20230615
输入的数据为:y = 2023, m = 6, d = 15
请输入数据:2023061512
输入的数据为:y1 = 2023, m1 = 61, d1 = 51
请输入数据:2021a12310.315
输入的数据为:a = 2, b = 
, c = 2021.000000

实际效果如下图:

image-20260206144038502

这里我们直接分析一下:

第一次输入:20230615↙,这个时候我们需要按%4d%2d%2d 的格式读取数据,这样会以此得到 y = 2023,m = 06,d = 15,这是没问题的。

第二次输入:2023061512↙,这次我们按照%4d%3d%2d 的格式读取,会得到 y1 = 2023,m1 = 061,d1 = 51,这也是没问题的,这个时候其实缓冲区还剩下 2 这个数字。

第三次输入:2021a12310.315↙,我们按照%d%c%f 的格式来读取,理论上我们应该获取到什么数据?我们实际验证一下吧:

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int a;
	char b;
	float c;
	printf("请输入数据:");
	scanf("%d%c%f",&a,&b,&c);
	printf("输入的数据为:a = %d, b = %c, c = %f\n", a, b, c);
	return 0;
}

我们同样输入 2021a12310.315↙,会得到以下输出:

c
请输入数据:2021a12310.315
输入的数据为:a = 2021, b = a, c = 12310.315430

可是上边我们实际得到的是:

image-20260206144202774

这是为什么?原因就在于我们第二次输入的时候,缓冲区还剩下一个 2 和 ↙(换行),它俩直接作为最后一个 scanf 的输入,所以最后依次输入的时候 scanf 只会读取一个%f 格式的数据,由于 2021 后边是字符 a,所以%f 就读取到了 2021,最后做了强制类型转换,就输出了上边的数据啦。

注意事项

(1)输入数据时,要格外注意遇以下情况认为该数据结束:遇 < space > 、 Tab 、或 Enter ;遇非法输入;遇宽度结束。

(2)一定要注意上一次的数据输入是否还有余留,可能会导致后续输入出现问题,具体处理方法看后文。

4.5 最后的换行的处理实例

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int x;
	char ch;
	scanf("%d", &x);
	// getchar();     /* 加上此行,吸收 <Enter> 或者可以用 scanf("%*c%c", &x); */
	ch = getchar();
	printf("x = %d, ch=%d\n", x, ch); /* \n 的十进制为 10 */

	return 0;
}

image-20260206144543014

5. scanf 缓冲区处理

很多时候,我们输入了一串数据后,又继续使用 scanf 输入,但此时缓冲区可能会有上次遗留下来的其他字符,包括换行符,这会对之后的输入造成影响的。我们就用前边《缓冲区的多余字符》一小节的笔记中的例子来做对比

5.1 getchar 处理

5.1.1 处理方式

我们可以使用 getchar() 来吸收缓冲区剩下的字符:

c
char ch;
while ((ch = getchar()) != EOF && ch != '\n') ; //清除缓冲区的内容

说明】 输入流中多余的字符均可通过此行代码处理。

5.1.2 使用实例
c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int y,m,d;
	int y1,m1,d1;
	int a;
	char b;
	float c;
	char ch;

	printf("请输入数据:");
	scanf("%4d%2d%2d", &y, &m, &d);
	printf("输入的数据为:y = %d, m = %d, d = %d\n", y, m, d);

	while ((ch = getchar()) != EOF && ch != '\n');

	printf("请输入数据:");
	scanf("%4d%3d%2d", &y1, &m1, &d1);
	printf("输入的数据为:y1 = %d, m1 = %d, d1 = %d\n", y1, m1, d1);

    while ((ch = getchar()) != EOF && ch != '\n');
    
	printf("请输入数据:");
	scanf("%d%c%f",&a,&b,&c);
	printf("输入的数据为:a = %d, b = %c, c = %f\n", a, b, c);
	return 0;
}

然后我们按照相同的方式输入数据,会得到以下输出:

image-20260206151554837

可以发现输入都正常了,但是吧最后的浮点型数据多了个 430,,目前还不是很清楚为什么,不过问题不大,感觉像是打印的时候没有保留小数位数,应该算是多打印了内存中的一些其他数据,其实可以自己通过格式化字符串的时候那个精度来截断一下就好了。

5.2 scanf 自行处理

5.2.1 处理方式

我们还可以用 scanf 自己来处理:

c
scanf("%*[^\n]%*c");/* 遇到'\n'结束读取输入的字符, 并将其读到的数据抛弃, 然后再抛弃一个字符(其实这个字符是'\n'),此时缓存中不存在任何字符 */
  • * :这里的星号 * 表示读入某类型的内容,但是这个内容不保存到变量里,所以后面不需要对应的参量;
  • [] :里边的内容是只读入限定读入的字符,如 [abcd] 指的是只读入 abcd 的字符;
  • %*[^\n] :表示读入除了回车之外的字符以及读入一个字符后不保存,只有这样,才不会把输入回车吸收,导致不能退出程序;
  • %*c :是读入最后一个没有读入的回车;
5.2.2 使用实例
c
#include <stdio.h>
int main(int argc, char *argv[])
{
	int y,m,d;
	int y1,m1,d1;
	int a;
	char b;
	float c;

	printf("请输入数据:");
	scanf("%4d%2d%2d%*[^\n]%*c", &y, &m, &d);
	printf("输入的数据为:y = %d, m = %d, d = %d\n", y, m, d);

	printf("请输入数据:");
	scanf("%4d%3d%2d%*[^\n]%*c", &y1, &m1, &d1);
	printf("输入的数据为:y1 = %d, m1 = %d, d1 = %d\n", y1, m1, d1);

    printf("请输入数据:");
	scanf("%d%c%f%*[^\n]%*c",&a,&b,&c);
	printf("输入的数据为:a = %d, b = %c, c = %f\n", a, b, c);
	return 0;
}

按照前边一样的输入方式,可以得到以下输出情况:

image-20260206151804131

可以发现和前面一样,获取到的数据也都正常的。

6. scanf 其他用法实例

6.1 读取带空格的字符串

我们使用 scanf 的话,也想读取带空格的字符串的话,可以使用这样的格式:

c
scanf("%[^\n]%*c",str); /* 遇到'\n'结束读取输入的字符, 并存入 str, 然后再抛弃一个字符 */

例如,

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	char str[30];
	while (1)
	{
		printf("请输入字符串:");
		scanf("%[^\n]%*c", str); //读到'\n'结束读取
		printf("输入的字符串为:%s\n", str);
	}
	return 0;
}
  • %*c :是读入最后一个没有读入的回车

【注意事项】后边不加 %*c 的话,依旧可以读取带空格的字符串,但是若是在一个循环中的话,会陷入死循环,至少我测试的时候是这样的,比如说上边的例子,若没有 %*c 的话,就会一直执行这一句

c
printf("输入的字符串为:%s\n", str);

6.2 读取特定字符后的字符

c
char ch;  
scanf("%*[^a]%*c%c",&ch); /* 遇到字符 a 停止读取,并舍弃 a, 将 a 后的一个字符存入变量 ch */

例如,

c
#include <stdio.h>
int main(int argc, char *argv[])
{
	char ch;
	printf("请输入字符:");

	scanf("%*[^a]%*c%c", &ch); /* 遇到字符 a 停止读取,并舍弃 a, 将 a 后的一个字符存入变量 ch */
	printf("输入的字符为:%c\n", ch);
	return 0;
}

我们编译运行的话,会有以下情况

shell
请输入字符:sdaddsa sa d
输入的字符为:d

四、fscanf

1. 函数说明

在 linux 下可以使用 man 3 fscanf 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fscanf(FILE *stream, const char *format, ...);

函数说明】 该函数从流 stream 读取格式化输入,即从文件格式化读取数据,后边学习标准 IO 的时候会再详细学习。

函数参数

  • stream : FILE * 类型,为文件指针变量。
  • format : char * 类型,为格式化字符串,与 scanf 一致。

返回值】 int 类型,如果成功,该函数返回成功匹配和赋值的个数,如果到达文件末尾或发生读错误,则返回 EOF 。

使用格式】 一般情况下基本使用格式如下:

c
FILE * fp;
int year, month, day;
fp = fopen("file_name", "r");
fscanf(fp,"%d-%d-%d",&year, &month, &day);

注意事项】 如果将 fp 设置为 stdin ,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同。

2. 使用实例

后边学习标准 IO 会再详细学习。

五、sscanf

1. 函数说明

在 linux 下可以使用 man 3 sscanf 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int sscanf(const char *str, const char *format, ...);

函数说明】 该函数从字符串读取格式化输入,表示我们可以通过此函数从一个字符串中格式化的读取我们要的格式数据。

函数参数

  • str : char * 类型,是格式化输入数据的来源。
  • format : char * 类型,为格式化字符串,与 scanf 一致。

返回值】 int 类型,如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF 。

使用格式】 一般情况下基本使用格式如下:

c
char buff[100] = "2022-5-4";
int syear, smonth, sday;
sscanf(buff,"%d-%d-%d",&syear, &smonth, &sday);

注意事项】 none

2. 使用实例

后边学习标准 IO 会再详细学习。