当前位置: 首页 > news >正文

学习小发现-04-如何从字符串中提取数字并转换为整型输出、如何在%d输入内容中判断整型并只读取数字以整型输出、scanf的各种用法

学习小发现 - 03 - 如何判断输入的是不是整型数据并重新输入,纠错(异常)scanf与strcmp,_阿秋的阿秋不是阿秋的博客-CSDN博客

接续上面这篇文章的问题。

        我先说明一下吧,这玩意我主要写的是思考过程,如果想直接看结果,点目录里的成果就行,我觉得这个也没什么实际意义,就是自己突发奇想了一下。也许会有更简便的方法或者函数,但是我觉得思考的过程还是很重要的。

目录

如何在%d输入内容中判断整型并只读取数字以整型输出:

成果

如何从字符串中提取数字并转换为整型输出

成果

scanf的多种用法

scanf如何规定读取个数

scanf如何清空缓冲区

scanf如何指定读取特定字符

scanf如何设置不想读取的字符

scanf如何丢弃读取的字符 - (清理缓冲区)

 总结:


如何在%d输入内容中判断整型并只读取数字以整型输出:

已有的代码

int main()
{
	printf("请输入数字:>");
	int age = 0;
	int tmp = 0;
	tmp = scanf("%d", &age);
	while (tmp == 0)
	{
		while (getchar() != '\n');
		printf("请输入数字:>");
		tmp = scanf("%d", &age);
	}
	return 0;
}

这个代码只能第一个输入字符不是数字字符,便清空缓冲区,重新输入的效果,但是没办法达到只输入一次并读取数字的效果。

根据这个想法,我写了下面这段代码,能够达到只输入一次并只读取数字的效果。

int main()
{
	printf("请输入数字:>");
	int age = 0;
    int ret = 0;
    int tmp=0;
	scanf("%d", &age); //scanf不会读取缓冲区的\n
	//如果缓冲区只剩下\n意味着,前面的都是%d形式的内容
	while ( ( ret=getchar() ) !='\n' )  //用getchar判断是否读取完毕,未读取完进入循环
	{
		if (ret < '0' || ret>'9') //若读取不是0-9内的字符,便继续读取
			continue;
		tmp = scanf("%d", &age); 	
	}
	printf("数字是%d\n", age);

	return 0;
}

 但是,问题又来了,因为getchar是一个字符一个字符读取,且读取后的字符不会在缓冲区内,所以只会读取第一个数字字符后的字符,也就是9。不信你看下面这个,有789,却只读取了89.

然后我就在想,能不能把这个数字放回缓冲区里,就学到了ungetc这个函数可以把数字放回缓冲区原来的位置。可以达到输入8789,读取8789的效果了。

 但是问题又来了,如果第一个就是数字字符,每个数字字符之间都有其他字符,那会发生什么?

 哦~只会读取最后一个数字字符

因为getchar是一个字符一个字符地读取啊。

而scanf是遇到不符合格式的内容便不再读取,所以便只读取了8。

我就又在绞尽脑汁地想,那把每次getchar读取到的数字字符存放到一个地方,然后在统一拿出来按照整型方式输出不就可以了

但是应该存到哪里去呢?

我想到了数组,因为数组是同一类型元素的集合,便正好适合保存我们读取到的数字字符。

于是便写出了这段代码。

int main()
{
	printf("请输入数字:>");
	int age = 0;
    int tmp = 0;
	int ret = 0;
	int i = 0;
	int j = 0;
	int age_tmp[100] = { 0 };
	tmp = scanf("%d", &age); //scanf不会读取缓冲区的\n
	//如果缓冲区只剩下\n意味着,前面的都是%d形式的内容
	while ( ( ret=getchar() ) !='\n' )  //用getchar判断是否读取完毕,未读取完进入循环
	{
		if (ret < '0' || ret>'9') //若读取不是0-9内的字符,便继续读取
			continue;
		ungetc(ret, stdin); //stdin可以理解为缓冲区
		scanf("%d", &age_tmp[i++]);
		//后置++为了方便下个字符放进来
	}
//以整型方式输出
	age = 0;
	for (j = 0; j < i - 1; j++)
	{
		if (!age)
			age = age_tmp[j] * 10 + age_tmp[j + 1];
		else
			age = age * 10 + age_tmp[j + 1];
			
	}
	printf("数字是%d\n", age);
	return 0;
}

先演示一下效果,

 可以达到从其中提取数字并以整型方式输出的效果了!

问题1:但是,有个致命问题,如果第一个输入的是整型,那么被读取到的整型不会再进入循环。

问题2:还有一个问题,如果输入的字符是连续的两位整型(比如89),那么在循环中读取时候,就是把89放到了一起,就不符合我们后面设定的整形转换输出方式了。

就像下面这样。(第二个问题的图我就不放了)

那么就需要多两个步骤了,1-把读取到的字符单个放到数组中,2-把循环中的scanf改为一次只读取一个整型,这不就好了。

说起来,你没发现我改代码的时候,一直有个tmp变量没用到吗?它接收的是scanf的返回值,也就是scanf按照要求格式读取成功的字符个数。

那这个变量就很适合我们用了。

在写的过程中,我觉得太长了,便直接把这个判断过程写到了函数中。起名叫 judge函数。那么主函数区域就剩这些了。

int main()
{
	printf("请输入数字:>");
	int age = 0;
	int tmp = 0;
	tmp = scanf("%d", &age); //scanf不会读取缓冲区的\n
	//如果缓冲区只剩下\n意味着,前面的都是%d形式的内容
	judge(tmp, &age);
	printf("数字是%d\n", age);
	return 0;
}

顺便一提:我觉得tmp这个变量接收的是scanf的返回值还是很有用的,不能直接根据age的值来判断有没有读取成功,因为age可以是任何数字0,8,9,98……

成果

现在我们写好了,函数递归部分看不懂的点这个链接。看代码演示那个标题。

从0开始学c语言-14-关于(3)函数递归、递归与迭代、栈溢出、练习求第n个斐波那契数、用递归思想求字符串的长度_阿秋的阿秋不是阿秋的博客-CSDN博客

int transfer(int* age_tmp, int age)
{
	//其实就是输入123并输出1、2、3到数组中
	int i = 0;
	if (age > 9)
	{
		i = transfer(age_tmp, age / 10); //接收下标
	}
	age_tmp[i] = age % 10;
	return ++i;
}

void judge(int tmp, int* age)
{
	int ret = 0;
	int i = 0;
	int j = 0;
	int age_tmp[100] = { 0 };

//如果读取成功(tmp为真),把读取数字 [单个] 放到数组中
	if(tmp) 
		i = transfer(age_tmp, *age); //我写成了函数,并且用i接收函数返回的数组下标
//读取后面部分
	while ((ret = getchar()) != '\n')  //用getchar判断是否读取完毕,未读取完进入循环
	{
		if (ret < '0' || ret>'9') //若读取不是0-9内的字符,便继续读取
			continue;
		ungetc(ret, stdin); //stdin可以理解为缓冲区
		scanf("%1d", &age_tmp[i++]); //每次只读取一个%d
		//后置++为了方便下个字符放进来
	}
//整型方式输出
	*age = 0;
	for (j = 0; j < i - 1; j++)
	{
		if (!*age)
			*age = age_tmp[j] * 10 + age_tmp[j + 1];
		else
			*age = *age * 10 + age_tmp[j + 1];

	}
}
int main()
{
	printf("请输入数字:>");
	int age = 0;
	int tmp = 0;
	tmp = scanf("%d", &age); //scanf不会读取缓冲区的\n
	//如果缓冲区只剩下\n意味着,前面的都是%d形式的内容
	judge(tmp, &age); //一定要取地址
	printf("数字是%d\n", age);
	return 0;
}

如何从字符串中提取数字并转换为整型输出

有了上面的基础,便只需要搞清楚字符串中的数字和ASCII码值有什么关系变可以做到我们想要的效果了。

你会发现,这个东西减去0字符的码值便正好是他本身的数字。

然后就写出来这段代码了。

成果

#include <stdio.h>
#define len 100
int main()
{
	char A[len];
	int B[len] = { 0 };
	int i, j,sum= 0;
	//输入获取字符数组A
	gets(A);

	//将A中的数字按照顺序放到B中,并记录放进去了几个数
	j = 0;
	for (i = 0; i < len; i++)
	{
		if (A[i] >= '0' && A[i] <= '9')
		{
			//放到A中的是ASCII码值,减去'0'会正好是这个int数字
			B[j++] = A[i] - '0';
			//后置++为了让下一个的下标正好对应要放进来的数字下标
		}
	}
	//假设我们输入字符串是asd7jj9k8
	//那么现在放到B数组中的是7,9,8
	//分别对应下标0,1,2
	//此时的j下标是3,正好是放进来数字的个数
	//要以整型方式进行输出
	for (i = 0; i < j - 1; i++)
	{
		if (!sum)
			sum = B[i] * 10 + B[i + 1];
		else
			sum = sum * 10 + B[i + 1];
	
		//798
		//sum=0时
		//sum=7*10+9=79
		//sum=79*10+8=798
		//或者从最后一位运算也行
	}
	printf("%d\n", sum);
}

简单吧,有了上面的基础以后就变得超级简单。

顺便一提,其实在探索的过程中我还去学了scanf的用法,发现了新天地,这里放着保存一下。

scanf的多种用法

scanf如何规定读取个数

scanf("%2d", &age);

其他形式也是在%后加数字即可。

scanf如何清空缓冲区

没有清空缓冲区的代码

int main()
{
	int a = 0;
	char ch[20] = { 0 };
	printf("请输入数字");
	scanf("%d", &a);
	printf("%d\n", a);

//若输入不符合格式的内容,应清空缓冲区

	printf("请输入字母");
	scanf("%s", ch);
	printf("%s\n", ch);
}

代码效果

 加上这两行代码到上面那段代码注释的地方

scanf("%*[^\n]"); //将\n前面所有字符清空
scanf("%*c"); //将最后剩下的换行符清空

注:这两行代码不能合并起来,如果缓冲区只剩下\n,\n的前面没有其他字符,不符合 将\n前面所有字符清空 的条件。

scanf如何指定读取特定字符

scanf("%[asdf]",ch); //只读取asdf
scanf("%[0-9]", age);//对应 ASCII码值左边不能大于右边

%[a-zA-Z] 读取大写字母和小写字母
%[a-z-A-Z0-9] 读取所有英文字母和十进制数字
%[0-9a-f] 读取十六进制数字

注:这意味着scanf遇到规定外的字符就不再读取。

如:scanf("%[asdf]",ch);  //只读取asdf

输入:adfssdasklas

输出:adfssdas

可以看到,即使后面有符合规定范围内的字符 as 也不会被读取到。

scanf如何设置不想读取的字符

%[^\n] 匹配除换行符以外的所有字符,遇到\n就停止读取,达到了读取一行的效果
%[^0-9] 匹配十进制数字以外的所有字符,遇到0-9内的字符就停止读取

%20[^0-9\n] 读取一行不能包含十进制数字的字符串,且长度不超过20

注意第一个代码,这个效果相当于gets函数读取一行的能力。

scanf如何丢弃读取的字符 - (清理缓冲区)

%*d读取一个整型并丢弃
%*[a-z] 读取小写字母并丢弃
%*[^\n] 将\n以外的字符全部丢弃

例子:

scanf("%*d%d",&n);

 总结:

scanf完整控制字符串的写法:
         % {*} {width} type

{}可不写
type就是读取什么类型的数据
width表示最大读取宽度
*丢弃读取到的数据,可有可无

相关文章:

  • Python中的闭包
  • Java知识【继承中的成员访问特点】
  • <Linux进程控制(2)>——《Linux》
  • 嵌入式软件编程模式
  • VL8 使用generate_for语句简化代码
  • 从零开始搭建uni-app框架的小程序开发环境
  • 【web】TCP/UDP协议详解(字节二面:TCP三次握手、四次挥手)
  • C++打怪升级(二)- 引用详解
  • MongoDB的日志目录被删除了,导致无法启动:(code=exited, status=1/FAILURE)
  • 数据我爬定了,限流也挡不住,我说的
  • 可持久化线段树
  • 计算机网络-网络层篇-子网划分
  • DataX 初识
  • 工程项目管理——第十章 软件项目团队计划
  • 基于Java+SpringBoot+Thymeleaf+Mysql摄影图片分享网站系统设计与实现
  • Go 语言编译器的 //go: 详解
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • Mocha测试初探
  • PAT A1120
  • 构建工具 - 收藏集 - 掘金
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 学习Vue.js的五个小例子
  • 《码出高效》学习笔记与书中错误记录
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • 2017年360最后一道编程题
  • 回归生活:清理微信公众号
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #DBA杂记1
  • (13):Silverlight 2 数据与通信之WebRequest
  • (Java)【深基9.例1】选举学生会
  • (Java数据结构)ArrayList
  • (Ruby)Ubuntu12.04安装Rails环境
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (转)拼包函数及网络封包的异常处理(含代码)
  • ***通过什么方式***网吧
  • .Net core 6.0 升8.0
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .Net Core与存储过程(一)
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET 解决重复提交问题
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [ C++ ] 继承
  • [1204 寻找子串位置] 解题报告
  • [codevs 1288] 埃及分数 [IDdfs 迭代加深搜索 ]
  • [corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape
  • [go] 策略模式
  • [Latex] Riemann 问题中的激波,接触间断,膨胀波的 Tikz 绘图
  • [Luogu P3527BZOJ 2527][Poi2011]Meteors(整体二分+BIT)
  • [ndss 2023]确保联邦敏感主题分类免受中毒攻击
  • [Notice] 朋友们,blog更新http://jiang-hongfei.spaces.live.com