【Linux】手把手带你写出进度条小程序
文章目录
- 一、前置知识
-
- 回车和换行
- sleep与缓冲区
- 二、倒计时小程序初版
-
- 倒计时数字示例
- 完整实现
- 三、倒计时小程序进化版
一、前置知识
回车和换行
回车和换行是两个概念,回车是指把光标移动到当前行开头,换行是指将光标从当前行开头移动到下一行开头。换行在语言层面上是 \\n,它是实际上会执行回车和换行两个动作,因为单纯的换行没有意义。回车是 \\r,它只会执行回车这一个动作。
sleep与缓冲区
sleep是一个C语言的库函数,头文件见上图,作用是会休眠若干秒。
现象:上面的左边部分的代码会先将字符串打印在显示器上,然后休息两秒程序结束,右边部分的代码会先休息两秒,然后打印字符串同时程序结束。
这里我们直接说造成这个现象的结论,具体原理等后面到文件系统时小编再详细介绍,第二个代码是因为在程序sleep期间,字符串还处在C语言缓冲区中,我们暂时把缓冲区当作一段内存空间。所以printf函数并不是把数据直接打印到显示器上,而是先把数据写到缓冲区了,等待合适是时机再把缓冲区里的内容刷新到显示器上。
缓冲器的刷新策略有两点:
1、当在字符串中遇到 \\n 或者 \\r\\n 时会将 \\n 前面的内容全部刷新到显示器。
2、程序退出时,缓冲区里的数据会被刷新到显示器。
如果我们不想让程序遇到\\n或者程序结束才刷新缓冲区、想让在缓冲区的内容强制刷新到显示器上该什么做呢?这里就需要用到fflush来手动刷新缓冲区,它的作用是刷新一个流,这里我们要刷新到显示器上所以需要传标准输出流给fflush.
所以在print后面跟上fflush就可以强制刷新缓冲区了,不用等到程序结束自动刷新。
为什么这里会有引入fflush?因为这里sleep了!
二、倒计时小程序初版
倒计时数字示例
这是倒计时的完整版代码,小编来解释一下。首先我们要挨个把10 9 8…打印出来,也就是刷新到显示器上,不经过之前介绍的两种刷新策略,而是强制刷新,就需要用到fflush。这里因为我们是从10开始倒计时的,所以需要定制占位符 %d 的输出格式,不然的话打印结果就会变成:10 90 80 70…,也就是回车后打印下一个数字只会覆盖一个字符,这也可以印证printf是格式化打印出一个一个的字符,而不是整数。2是指打印出的内容最少占两个字符宽度,-是指把输出的内容左对齐,因为默认是右对齐的。
完整实现
我们Process板块采用分离编译,把方法的声明放在Procee.h,方法的实现放在Procee.c,还需要一个main.c 来测试代码。
进度条的图形用打印字符串来表示,进度条满后字符串中右100个#字符,当进度条每增加百分之一时就把字符串刷新一次,并在字符串末尾增加一个#。
上面是完整代码,小编来介绍其中涉及的奥秘:
1、字符串需要用一个字符串数组来维护,因为C语言字符串末尾有\\0,所以开101个字符大小的空间。为了方便起见,可以将字符数组一开始全部初始化为\\0,以免每增加一个#还要考虑在后面添加\\0,这里就要用到memset函数来整体初始化:
2、我们在打印时需要预留出100个字符的空间,而且是从右向左打印,需要左对齐,所以定制占位符 %d 的输出格式%-100s。
3、sleep最快也只要间隔1秒,因为sleep的参数类型是整型。我们想让进度条间隔时间短一点就需要用到usleep,它的间隔时间单位是微秒。
(1秒等于1000毫秒, 1毫秒等于1000微秒) 4、print里占位符%%代表输入一个%。
5、因为\\是转义符,所以字符串中\\\\代表一个\\,字符串\"|/-\\\"的字符串长度为4(不计算\\0)。
三、倒计时小程序进化版
我们前面实现的倒计时小程序初版有个问题,就是它没有适用场景,只是一个空壳子,所以接下来我们要实现的进化版就要把它应用到一个具体的场景。场景我们就选择进度条最常出现的下载文件,但是我们还没有网络相关的知识,所以只能把下载过程模拟出来。
main.c:
1、target_file_size表示待下载文件的大小,speed代表下载速度,current_total表示目前下载总量。
2、当已下载量等于总量时,while循环里只用做刷新,所以加个if判断。
3、download函数中size表示下载总量,split表示单位时间内下载的内容大小,这里的单位时间就是usleep的一次间隔时间。
process.c:
1、Fflushprocess函数中target表示需要下载的总量,current表示目前已下载的内容大小。
2、在Fflushprocess函数里定义进度条最后面的光标旋转要和传来的参数current解耦,因为现实下载有可能会出现网络问题导致main函数里speed为0,然后间接导致传来的参数current一直不变,而如果光标旋转和current有关的话光标就会停止旋转,所以处理光标旋转的思路应该是只要在调用Fflushprocess函数光标就旋转,表示进度条还是在工作,只是因为网络原因没有在下载数据。所以定义一个静态变量index,调用一次函数index++,光标就变化一次。(定义成全局变量也行,只要不要每次出Fflushprocess函数被销毁就行了,因为index值需要一直改变光标才会一直旋转。)
3、rate是current除以target,但是它除出来是小数,后边rate的值是进度条里的百分数,所以需要将它乘100。
4、cnt是rate取整后的结果,cnt表示当前进度条有多少个#,后面for函数的作用是每次调用Fflushprocess函数都刷新一次当前#的数量。
process.h:
以上就是小编分享的全部内容了,如果觉得不错还请留下免费的赞和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~