关于C语言输入输出问题

目录

引言

1 理论初识

1.1scanf

2 利用scanf输入_程序讲解

2.1 只输入int型

 2.2 输入所有形式

 2.3 循环判断每个字符

 3.字符串输入函数

3.1 gets()

3.2 fgets()

4.字符输入函数

4.1 getchar()

5.字符和字符串输出函数

5.1 字符输出

5.2 字符串输出

5.2.1 puts()

5.2.2 fputs()


引言

本位列举了个别C语言scanf输入时,对输入类型不合法的判断的写法,或其它特殊的输入,以及利用其它函数比如fgets()等的输入的用法,另外还提及了一点输出的用法,相信读完这篇文章,大家对输入的写法会更加得心应手,千万不要小看这个点哦,它往往是建房的基础。话不多说,接下来请看下面几种程序的讲解。

1 理论初识

1.1scanf

这里写的内容可以先不看,等看完下面的程序讲解再回来看这个,会更好的理解。

scanf是格式输入函数,它将读取的数据,根据其参数格式存储到相应的参数位置,这是最简单的用法。scanf函数将读取并忽略下一个非空格字符前遇到任何的空格字符,直到读取到一个非空格字符数据才会停止读取(这里的空格字符有:1.空格 2.换行符 3.制表符),因此,在我们使用scanf函数输入数据时,空格在scanf里面是否添加都不会影响scanf输入数据。

有一个点需要注意,当空格在读取数据之后放置时,我们需要再重新输入一个非空格字符的数据,否则函数将不会结束。

2 利用scanf输入_程序讲解

2.1 只输入int型

功能:输入一个%d ,若不符合此类型,重新输入,如果输入'!',退出循环

while(1)
{
if(scanf("%d",&n)==0){//执行循环 如果scanf返回值为0,即输入不符合所定义的格式(%d),继续if的判断
		if((ch=getchar())=='!') break;//使用getchar取出这个不符合定义的格式输入,如果它是!号,那么跳出循环,程序退出
		else{
			printf("input error\n");//否则输出input error
			continue;//然后结束本次循环,重新开始输入
		}
	}

 这里是利用scanf函数的特性,scanf实际上是有一个返回值,如果输入正确,它的返回值为1,如果有两个%d,且两个输入都是正确,那么返回值就是2;如果输入错误,那么他的返回值就是0。

下面举一个判断素数的例子,可以拿去验证,源程序如下:

#include<stdio.h>
#include<math.h>
int main(){
	int i;
	int n;
	int ch;

while(1){
	printf("please input:(n>3,'!'结束):");
	if(scanf("%d",&n)==0){
		if((ch=getchar())=='!') break;
		else{
			printf("input error\n");
			continue;
		}
	}

	i=2;
	int tmp=sqrt(n);
	while(i<=tmp){
		if(n%i==0) break;
		i++;
	}
	if(i<=tmp)
	{
		printf("%d not \n",n);

	}
	else printf("%d yes\n",n);
}
	return 0;
}

 下面是该程序运行结果:

 

 2.2 输入所有形式

scanf("%[^\n]",ch);

 %[^\n] 可以理解为除了enter键,即换行符'\n'之外,其它的都可以输入进去,'^':否 

如果利用scanf输入字符串时,如果直接写scanf("%s",arr);是不可取的,scanf这种情况下不能够读取空格,会自动跳过;而scanf("%[]",arr);的形式可以识别特定的字符集。

比如scanf("%[0-9]",arr);意思是只识别0-9之间的数字,一旦输入不在0-9之间,那么不再进行读取;在scanf中,* 是跳过相应的字符项,scanf("%*[a-zA-Z]%[0-9]",arr);意思为跳过前面的字母,在输出时只输出0-9,如下所示:

char arr[100];
scanf("%*[a-zA-Z]%[0-9]",arr);

 输出结果为:

abcde123456
123456

 如果对*不太理解,来看下面的一个例子:

#include<stdio.h>
int main()
{
	int a=0,b=0,c=0,d=0;
	scanf("%*d %*d %d %d",&a,&b,&c,&d);
	printf("%d %d %d %d\n",c,a,b,d);
	return 0;
}

 输出结果:

15 12 13 20
0 13 20 0

 一开始输入a=15 b=12 c=13 d=20,但是输入的&a,&b被跳过,读取的只有值:13,20,在输出过程中,我放的顺序是c,a,b,d以便于更好的理解,可以发现这个13是a的值,20是b的值,所以是读取的第一个值给a,读取的第二个值给b。 

下面写了一个字符串提取数字的程序

#include<stdio.h>
#include<string.h>
#include<ctype.h>//isdigit函数库
# define N 50
int main(){
int i=0,j=0;
char ch[N]={0};//定义字符数组
char s[N]="";//定义空的字符串数组
printf("please input one string and end with key_enter:\n");
scanf("%[^\n]",ch);
while(ch[i]!='\0'){//判断字符数组里的'\0',
if(isdigit(ch[i])){//使用循环判断每一个字符,isdigit:是不是数字,
s[j]=ch[i];//如果是,将这个数字存到新的数组里
j++;
}
i++;
}
printf("下面为提取到的数字:\n");
puts(s);
return 0;
}

程序运行结果:

 2.3 循环判断每个字符

这是一个判断输入是大小写或数字,并将大写转小写,小写转大写的程序,可以利用这个框架,如果是字母或数字,执行某种操作。

while((ch=getchar())!=EOF){//EOF为'ctrl+d',等于EOF结束循环
# include<stdio.h>
# include<ctype.h>
int main(){
	int ch;
	printf("please input:\n");
	while((ch=getchar())!=EOF){//EOF为'ctrl+d',等于EOF结束循环

		if(isalpha(ch)){//判断是否是字符

			if(isupper(ch)){//判断ch是否是大写字母
				     printf("Upper:%c\n",ch);
				     ch=tolower(ch);//转换成小写
				     printf("tolower:%c\n",ch);
				  // gets(ch);
				     continue;//如果不用continue,转换为小写后会自动进入下一个if,在这里终止此次if的作用
		
			}
			if(islower(ch))//判断是否是小写字母
				       printf("Lower:%c\n",ch);
					ch=toupper(ch);//转换成大写
					printf("toupper:%c\n",ch);


		}
		if(isdigit(ch)){//如果是数字
				printf("Digit:%d\n",ch-'0');//%d是输出字符的ASCII码值,所以要减去'0',得到数字
				printf("Digit:%c\n",ch);}//%c输出字符型
		/*putchar(ch);*/putchar('\n');

	}
return 0;
}

 在程序的运行过程当中,如果输入了非字母非数字的其它字符,不在任何一个if内,不做任何操作,程序继续往下走,走回循环可以继续输入:

 3.字符串输入函数

3.1 gets()

gets()函数的原型是:char *gets(char *str); 头文件:#include<stdio.h>

这个函数比较简单,只有一个参数,参数类型为char*,意思就是str可以是一个字符指针变量名,也可以是一个字符数组名

gets()函数的功能是从输入缓冲区中读取一个字符串,当读取到换行符时,或到达文件末尾(EOF)时,它会停止读取,并返回该指针;若发生错误,或者到达文件末尾时,还未读取任何字符,则返回NULL。表面上看来,使用gets()比scanf简洁,gets()函数即使输入的字符串中有空格也可以直接输入,而且!gets()自动将缓冲区的换行符取出来,然后丢弃,缓冲区不会遗留换行符。这就意味着,如果后面又要从键盘给字符变量赋值,不需要使用getchar(),去拿掉这个换行符,它已经被gets()取出来扔掉了。

但是,使用gets()非常危险,它不检查预留存储区是否能够容纳实际输入的数据,换句话说,gets()函数不会检查字符串的大小,只会遇见换行符或EOF才会结束输入,因此容易造成缓存溢出的安全性问题,使程序崩溃,所以现在一般不用这个函数。

3.2 fgets()

fgets()可以用来代替gets(),fgets()头文件:#include<stdio.h>

fgets()原型:char *fgets(char *s,int size,FILE *stream);

这里的s代表要保存到的内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名。size:需要读取的字符串长度,除了规定读取某个长度值,fgets()的第二个参数与数组的长度保持一致就可以,不用减一,系统会自动减一,补空字符;stream表示从何种流中读取,可以是标准输入流stdin,也可以是文件流,即从某个文件中读取;从键盘读取数据的话,就是从输入缓冲区中读取数据,即从标准输入流stdin中读取数据,所以第三个参数为stdin.

功能:从stream流中读取size个字符存储到字符指针变量s所指向的内存空间,返回值是一个指针,指向字符串中第一个字符的地址。下面我们看一个程序:

#include<stdio.h>
#define N 10
int main()
{
	char *p;
	char a[N];
	char b[N];
	p=a;
	char *q=b;
	printf("please input one array:\n");
	fgets(p,sizeof(a),stdin);
	printf("%ld\n",sizeof(a));
	printf("%s",p);
	printf("可以看到这里上面输入后自动换行\n");
	scanf("%s",q);
	printf("b:%s",b);
	puts("");
}

 从上面的程序输出结果来看,有需要注意的两点:

fgets()函数中,我给的是N为10个空间,但是输出abcdefghi,才9个,所以'\0'也算一个,输入完成按下enter键后,我并没有加puts("");进行换行,fgets()自己完成了换行;所以fgets()和gets()一样,最后的回车都会从缓冲区中取出来,只不过gets()是取出来丢掉,而fgets()是取出来自己留着,但总之缓冲区中已经没有回车了,所以在下次输入的时候不需要清空缓冲区。

第二点,我定义的10个空间,但是我输入超过10个,那么剩下的字符会存储在缓冲区中,在程序当中,打印完fgets()的字符后,我又进行了数组b的输入,但是并没有运行出来,而是直接打印结果,这个结果正是fgets()留在缓冲区的字符。

please input one array:
abcdefghijklmn
10
abcdefghi可以看到这里上面输入后自动换行
b:jklmn

还有一个点也要注意:

fgets()给字符数组赋值的时候,会把末尾的回车键也放入到字符数组中(前提是有地方放),字符数组最后一个字符一定是\0.

#include <stdio.h>
int main()
{
char arr[5];
char *p = arr;
fgets(p,5,stdin);
for(int i = 0;i < 5; ++ i)
 {
 if(arr[i] == '\0')
  printf("\\0");
 else if(arr[i] == '\n')
  printf("\\n");
 else
  printf("%c",arr[i]);
 }
 
 return 0;
}

来看输出结果:

可以看到,当输入的字符串小于我所定义的空间,在有空间的时候,这个'\n'也会放入字符数组中,如果没有空间,那这个'\n'就不要,但是'\0'肯定要,这是字符串结束标志毫无疑问。

 与fgets()基本相同的写法还有一个char *gets_s(char *s,int size),函数从stdin读取一行到s所指向的缓冲区,直到一个终止符或EOF,使用时比gets要安全,推荐用字符数组长度-1作为size(留空'\0').

4.字符输入函数

4.1 getchar()

getchar() 原型:int getchar(void)  头文件:#include<stdio.h>

我们可以看到getchar()的参数是void,而返回类型为int,这是因为getchar()返回的是字符的ASCII码值,也就是整型数值,而读取失败的时候,会返回EOF,遇到回车停止读取。这个函数函数在同一个时间内只会读取一个单一的字符,可以循环读取,以便从屏幕上读取多个字符。

功能:getchar()从缓冲区读取一个字符.

对getchar()的理解:

getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).当用户键入回车之后,getchar才开始从stdin流中每次读入一个字符.getchar函数的返回值是用户输入的第一个字 符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续 getchar调用读取.也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键.

下面来看代码:

#include<stdio.h>
int main()
{
	char a[10];
//	while(1)
//	{
	char ch=getchar();//输入字符
	putchar(ch);puts("");
//	}
	return 0;
}

 下面的第二次运行,是未加循环,可以看到当没有遇到换行符并输入多个字符是,只会输出一个第一个输入的字符,这说明getchar()每次只拿走一个字符,而剩下的字符还在缓冲区内;而我加入了循环,那么我还没有输入的情况下,putchar(ch)挨个的把我缓冲区的字符拿了出来。

5.字符和字符串输出函数

5.1 字符输出

putchar()

原型:int putchar(int char);

一次只能输出一个字符,可以采用循环输出解决,比如下文的puts(),可以用putchar()实现:

int puts(const char * string) 
{ 
    const char * t = string; 
    const char * v = string; 
    int i = 0; 
    while(*t!='\0') 
    { 
        i++; 
        t++; 
    } 
    int j = 0; 
    for(j;j<=i;j++) 
        putchar((v[j])); 
    putchar('\n');
    return 0; 
}

 再来看putchar()的一个例子:

#include<stdio.h>
int main()
{
	int r=50;
	putchar('0'+r);//字符0向下走r个ASCII码值
	//0的ASCII码值为48,加r=50为98,对应b,所以输出字符b
	
	puts("");
	putchar(49);//1的ASCII码值:49,所以输出1
	puts("");
	return 0;
}

 输出结果:

b
1

 C语言中的char类型底层就是int ,所以putchar()里面可以装一个数字,但是它实际上装的是ASCII码值,我们可以利用这个点,在写程序时,先将int做该做的运算,最后输出时,可以做到一个转换的效果。

5.2 字符串输出

5.2.1 puts()

puts() 输出字符串,将'\0'转换为回车换行,调用方式为:puts(str),其中str是字符串数组名或字符串指针,实际上,数组名就是指针。

原型:int puts(const char *str)  头文件:#include<stdio.h>

#include <stdio.h>
int main(){
 char str[]="hello world";
 puts(str); 
 return 0;
} 
 
/*output:
hello world

按任意键退出
*/

 注意这里有一个自动换行。puts()还可以从指定字符位置开始输出:

#include <stdio.h>
int main(){
 char str[]="hello world";
 puts(str+2); 
 return 0;
} 
 
/*output:
llo world

按任意键退出
*/

 注意puts()只能输出字符串,不能输出数值或者进行格式转换,即不能要求输出格式增加空格、换行(指的是输出内容的中间进行换行)等要求;

puts()可以将字符串直接写入,如:puts("hello world");

5.2.2 fputs()

fputs()也是字符串输出函数,其原型为:

#include<stdio.h>  头文件    int fputs(char *s,FILE  *stream)

s代表要输出字符串的首地址,可以是字符数组名或字符指针变量名。

fputs()与puts()的两点小区别:

1.puts()只能向标准输出流输出,而fputs()可以向任何流输出

2.使用puts()时,系统会在其后添加换行符,而使用fputs()时,系统不会自动添加换行符。

那么这是不是意味着使用 fputs() 时就要在后面添加一句“printf("\n");”换行呢?看情况!如果输入时使用的是 gets(),那么就要添加 printf 换行;但如果输入时用的是 fgets(),则不需要。

因为上面提到过使用 gets() 时,gets() 会将回车读取出来并丢弃,所以换行符不会像 scanf 那样被保留在缓冲区,也不会被 gets() 存储;而使用 fgets() 时,换行符会被 fgets() 读出来并存储在字符数组的最后,这样当这个字符数组被输出时换行符就会被输出并自动换行。

但是也有例外,比如使用 fgets() 时指定了读取的长度,如只读取 5 个字符,事实上它只能存储 4 个字符,因为最后还要留一个空间给 ‘\0’,而你却从键盘输入了多于 4 个字符,那么此时“敲”回车后换行符就不会被 fgets() 存储。数据都没有地方存放,哪有地方存放换行符呢!此时因为 fgets() 没有存储换行符,所以就不会换行了。

# include <stdio.h>
int main(void)
{
    char str[20];  /*定义一个最大长度为19, 末尾是'\0'的字符数组来存储字符串*/
    printf("请输入一个字符串:");
    fgets(str, 19, stdin);  /*从输入流stdin中读取19个字符到字符数组str中*/
    fputs(str, stdout);  //将字符数组的内容输出到输出流stdout中
    return 0;
}

输出结果是:
请输入一个字符串:i love you
i love you
Press any key to continue

我们看到读取 19 个字符足够存储“i love you”,所以 fgets() 最后会存储换行符。这样 fputs() 输出时这个换行符就能换行了。
下面再将读取的字符改小一点看看:

# include <stdio.h>
int main(void)
{
    char str[20];  /*定义一个最大长度为19, 末尾是'\0'的字符数组来存储字符串*/
    printf("请输入一个字符串:");
    fgets(str, 5, stdin);  //从输入流stdin中读取4个字符到字符数组str中
    fputs(str, stdout);  //将字符数组的内容输出到输出流stdout中
    return 0;
}

输出结果是:
请输入一个字符串:i love you
i loPress any key to continue

我们看到并没有换行。

这篇文章到此结束了,希望会对需要的人有所帮助

另外送给大家一句话:想要的东西太贵,喜欢的女人太美

好好努力吧!不要放弃!加油!

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y_寒酥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值