【Linux系统编程】Linux第一个小程序——进度条

文章目录

  • 1. 对回车(\r)和换行(\n)的理解
    • 1.1 概念理解
    • 1.2 测试
  • 2. 缓冲区的理解
    • 2.1 观察现象
    • 2.2 原因解释
  • 3. 倒计时小程序
  • 4. 进度条小程序
    • 4.1 基本思路及实现
    • 4.2 改进及优化
    • 4.3 增加百分比显示
    • 4.4 增加旋转光标
    • 4.5 给进度条配色

这篇文章我们一起来完成我们Linux中的第一个小程序——进度条
在这里插入图片描述

1. 对回车(\r)和换行(\n)的理解

1.1 概念理解

在C语言中,字符可以分为可显字符(printable characters)和控制字符(control characters)。

可显字符是指可以在屏幕或打印输出上显示的字符,它们包括数字、字母、标点符号、符号等。可显字符可以直接被用户看到,并且在文本处理、显示和通信中起到重要作用。
控制字符是一类在计算机中具有特殊含义的字符,它们通常不可见或只能以特殊方式显示。这些字符用于控制文本的格式、编辑和通信等方面。

这里我们要重点理解两个控制字符——\n\r

\r表示回车,即将光标移动到当前行的起始位置
\n表示换行,即将光标向下移动一行
但是我们平时用的比如C语言打印的时候加一个\n换行
在这里插入图片描述
或者在编辑文本的时候敲enter
在这里插入图片描述
他不仅进行了换行并且光标也移到了起始位置。
但是其实这是两个步骤,先移到下一行,再移动到起始位置。
不过呢,在常见的计算机系统中,换行通常会伴随回车操作。

1.2 测试

下面我们来测试几个程序。

首先我来写一个makefile,我们待会写完代码可以直接用:
在这里插入图片描述

然后我来写一个test.c

在这里插入图片描述
那这里面我就用到了换行\n

那我来运行看一下:

在这里插入图片描述
我们看到这里就成功打印出来了hello world,并且进行了换行(且伴随了回车)。
所以后面的命令提示符就打印到了下一行,并且在开头位置。

然后我们把\n换成\r试一下:

在这里插入图片描述
再来make然后运行
在这里插入图片描述
我们看到这次什么都没打印

那为什么啥都没打印呢?

那其实就是跟最后的\r有关系,printf打印的时候,前面的hello world都没问题,但是最后遇到\r(回车),就把光标移到了最左边起始位置。
所以后面打印命令提示符的时候就把hello world覆盖掉了。

2. 缓冲区的理解

下面我们来理解一下缓冲区的概念

缓冲区(Buffer)是计算机系统中用于临时存储数据的一块内存区域。它通常用于处理输入和输出操作,以提高效率和性能。
缓冲区相当于一个中间层,位于数据的来源和目的地之间。当进行输入或输出操作时,数据先暂时存储在缓冲区中,然后再批量地传输到目标位置或从源位置读取出来。这样可以减少对源位置或目标位置的直接读写次数,从而提高数据传输效率。

2.1 观察现象

下面我们还是来观察两个程序

先看第一个:

在这里插入图片描述
这里用了一个函数sleep
sleep() 函数用于在程序中暂停执行一段时间,sleep() 函数的参数是以秒为单位的等待时间。它的作用是让程序进入休眠状态,停止执行指定的时间间隔,然后再继续执行后续的代码。
在Linux或UNIX系统中,可以包含 <unistd.h> 头文件,使用 sleep() 函数。而在Windows系统中,可以包含 <windows.h> 头文件,使用 Sleep() 函数。
然后我们观察一下结果
我这里给的是截图,这里如果大家自己测试可能会观察的更好一点
在这里插入图片描述
我们看到,这里先打印了hello world,然后进行休眠(因为我们使用了sleep)
在这里插入图片描述
休眠结束,就打印了新的命令行。

然后我们看第二个:

跟上面的区别就是我把\n去掉了
在这里插入图片描述
然后我们再来运行
在这里插入图片描述
这次我们会观察到它是先休眠
在这里插入图片描述
休眠结束然后才打印hello world,并且新的命令行直接跟在hello world后面,因为我们没有换行。

那通过对比两次程序的结果,我们能得出:

带\n的时候是先打印hello world,后休眠;而不带\n是先休眠,后打印hello world。
那这样的话,不带\n的时候,好像是先执行了sleep函数,然后才执行printf去打印。

是这样吗?

当然不是的,我们知道程序默认是按照从上到下顺序执行的。
所以肯定是先执行printf,再执行sleep,毋庸置疑。

2.2 原因解释

那为什么我们看到的是先休眠,后打印,两个程序打印的时机为什么不一样呢?

🆗,我们上面有提到缓冲区的概念:
缓冲区相当于一个中间层,位于数据的来源和目的地之间。当进行输入或输出操作时,数据先暂时存储在缓冲区中,然后再批量地传输到目标位置或从源位置读取出来。
也就是是,不管我们有没有加\n,我们的hello world这个字符串都会被暂存到缓冲区里面。
那为什么两个程序打印的时间不一样呢?
原因其实是因为两个程序的缓冲区刷新的时机不同。
在大多数编程语言和操作系统中,缓冲区被用来暂时存储要输出或被读取的数据,直到达到一定条件后才会将其发送到目标位置(如屏幕、文件、网络等)。这个条件通常是缓冲区满了、遇到换行符、或者主动进行缓冲区刷新的操作。
程序结束时,通常会自动刷新输出缓冲区。这意味着在程序执行完成后,输出缓冲区中的所有数据将被写入到相应的输出设备(如终端或控制台)并在屏幕上显示出来。
所以我们可以认为,遇到\n的时候就会触发缓冲区刷新操作。
而程序结束也会刷新缓冲区。

那现在,我想大家就明白了,为什么上面两个程序的结果有差异?

第一个程序我们加了\n,所以执行printf时遇到\n就会刷新缓冲区,那么hello world就直接显示到了显示器上。
所以是先打印,后休眠。
而第二个程序,没有\n,我们也没有手动刷新缓冲区,所以直到程序结束是刷新缓冲区,hello world 才会显示到显示器上。
因此是先休眠,后打印。

那有了缓冲区的理解,我们再来看上面最开始演示的那个程序:

在这里插入图片描述
现在在hello world后面加一个\r。
我们运行看看
在这里插入图片描述
在这里插入图片描述
休眠结束啥没打印,新的命令提示符就出来了。
那这个我们上面其实解释过,因为\r的缘故,使得光标移到了最左边起始位置,所以后面的命令提示符就把先打印出来的hello world覆盖了。
那我现在修改一下:
在这里插入图片描述
fflush这个函数可以刷新缓冲区给,那这样就相当于我们提前刷新了一下缓冲区,这样休眠就在打印后面了,方便我们观察。
此时我们再运行
在这里插入图片描述
在这里插入图片描述
这下我们就看清楚了,并不是啥也没打印。
而是hello world打印之后,光标回到了最左边,然后后面打印的命令提示符就把hello world覆盖掉了。
当然如果把\r去掉,就不会被覆盖了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 倒计时小程序

那基于上面讲的内容,我们一起来实现一个倒计时小程序练练手:

怎么做呢?

在这里插入图片描述
大家看这样写是不是就行了
这里从9开始倒计时,i从9到0,循环打印,\r保证每个数字打印之后都把光标移到起始位置,fflush刷新缓冲区,这样使得每个数字可以分开显示,每次循环i都可以刷新出来,然后休眠1秒,显示下一个数字。
我们运行看一下
在这里插入图片描述
在这里插入图片描述
这个大家可以自己写写运行一下,截图看着不方便。
具体的效果就是从9开始,9、8、7、6、5、4、3、2、1、0一次交替显示。
在这里插入图片描述
在这里插入图片描述
但是当前这样写,最终0显示完之后,这一行就被新的命令行覆盖了。
所以我们可以加一个换行
在这里插入图片描述
在这里插入图片描述
这样最后倒计时这一行就不会被覆盖了。

但是呢,我们的程序还有一些问题

我们刚才倒计时9到0都是一个数,占一个位置,所以后面的刚好覆盖前面的,那如果是从10开始呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
效果就成这样了。
因为后面都是一位数,只能覆盖一个位置,后边的0就一直显示,不受影响。
实际上我们无论打印什么类型的数据,显示器上显示的内容都是一个个的字符,打印整数时,它们也会以字符的形式显示在屏幕上。计算机内部使用二进制表示整数,但在显示器上呈现给用户时,需要将其转换为对应的字符形式。

那怎么解决呢?也很简单:

我们指定域宽就行了。
printf可以用格式控制串"%md"输出域宽为m的十进制整数(默认左对齐,-m则右对齐)
在这里插入图片描述
然后我们再来运行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就可以了。

4. 进度条小程序

那我先来大致说一下我们最后要实现的一个进度条的样式:

就是一个大的【】,里面预留出来100个字符的空间,我们填充#,当然你也可以用其他的,1%就打印一个#,2%就两个,以此类推,后面可以显示一下具体是百分之几,随着#增加不断递增直到100%。

其实它大致的思路和上面的倒计时是一样的:

就是不断的显示并覆盖之前的内容。

那接下来我们就来实现一下。

我呢想给它写成一个多文件的形式:

在这里插入图片描述
我先创建这样3个文件。
在这里插入图片描述
先写进去这些内容。
然后把Makefile也写一下:
在这里插入图片描述

4.1 基本思路及实现

然后,我们来写实现进度条的函数process:

首先我们可以先开一个数组,把进度条需要的100个字符的空间预留出来。
那我们打印的时候可以直接打印#组成的字符串,那字符串的话就要再给\0开一个空间。
在这里插入图片描述
然后我们可以给buf数组全部初始化为\0,这样我们后续添加#就不用考虑\0的问题了。
在这里插入图片描述
然后我们循环打印并不断添加#就行了,当然我们这里还应该使用\r不断的回车,使每一次新打印的覆盖之前的,并且每次循环printf之后要使用fflush刷新缓冲区,这样才能每次循环都够打印出来内容,要不然程序结束之前一直留存在缓冲区。
在这里插入图片描述
我们运行看看效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 改进及优化

上面的实现,根据实际的运行效果我们可以发现两个问题:

首先第一个休眠时间设置成1秒有点长了,这样跑到100%需要100秒

所以我们可以选择把sleep函数换成usleep。
sleep的参数是以秒为单位的,而usleep是以微秒为单位的。
我们可以设置成0.1秒休眠时间
在这里插入图片描述
运行一下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这次速度确实快了

但是
第二个问题:进度条这一行显示完毕,新出现的命令行会把进度条的一部分覆盖掉。

怎么解决?
很简答,加个换行就行了
在这里插入图片描述
再看:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这次就没事了。

当然我可以加一个宏,这样后面替换进度条的样式就很方便:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后我们再修改一下,改成这种:

在这里插入图片描述
类似一个箭头,改一下代码:
在这里插入图片描述
看一下效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
但是这样最后停下来,还有一个箭头,好像有点不好看。
向前推进的时候显示箭头,100%的时候不显示,我们再来修改一下
加一个判读就行了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3 增加百分比显示

那一般进度条后面还有显示百分比,我们也来加一个:

在这里插入图片描述
运行一下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 增加旋转光标

然后我们再来在后面增加一个旋转光标:

可以通过循环显示这四个字符| / - \来模拟一个旋转的过程(注意\要用转义字符\\
在这里插入图片描述
我们来运行看看效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就可以了。

4.5 给进度条配色

在C语言中,可以使用ANSI转义序列来输出不同的颜色。ANSI转义序列是一系列的字符组合,用于控制终端的文本样式和颜色。

关于配色方案,网上可以找到很多相关的资料,大家有兴趣可以按照自己的喜好去配色,我这里简单演示一下:

在这里插入图片描述
比如我配个红色
在这里插入图片描述
在这里插入图片描述
来个绿色
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

还有背景颜色也可以设置:

在这里插入图片描述
试一下,来个青色
在这里插入图片描述

也可以同时设置字体颜色/前景颜色和背景颜色

在这里插入图片描述
我们来个字体红色,背景青色
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

大家可以按自己的喜好设置。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/22900.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kotlin中使用Room数据库(包含升降级崩溃处理)

目录 1.导入依赖库 2.数据实体类 3.数据访问对象 (DAO) 4.数据库类 5.调用DAO里面的“增、删、改、查”方法 6.数据库升降级处理 升级&#xff08;保存数据库历史数据&#xff09;&#xff1a; 升级&#xff08;不保存数据库历史数据&#xff09;&#xff1a; 降级&…

计网笔记--运输层(vital)

目录 1--运输层概述 2--端口号、复用与分用的概念 3--UDP和TCP的对比 4--TCP的流量控制 5--TCP的拥塞控制 6--TCP超时重传时间的选择 7--TCP可靠传输的实现 8--TCP的连接建立 9--TCP的连接释放 10--TCP报文段的首部格式 1--运输层概述 运输层的任务&#xff1a; 为运…

Yalmip工具箱使用教程(1)-入门学习

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;YALMIP 1.Yalmip工具箱的下载与安装 1.1下载 Yalmip的作者是Johan Lfberg&#xff0c;是由Matlab平台编程实现的一个免费开源数学优化工具箱&#xff0c;在官网上就可以下载。官方下载…

cmake 提前结束处理命令: return

有时候,我们有这样的需求,当处理到某个地方的时候,后面的我们都不想处理或者不需要处理的时候,就可以提前结束当前的处理逻辑,回到父级去处理.在C/C中,我们有break关键字跳出当前循环,continue关键字进入下一次循环,return关键字返回当前处理的函数. cmake也提供了break(),con…

场景图生成——RelTR训练自己的数据集

RelTR训练自己的数据集 省流量省时间版本框的标注关系的标注总的 前言Open Images V6的标注格式RelTR中使用的Open Images V6的数据标注格式具体步骤框的标注生成格式关系三元组的生成格式 结束语参考链接 省流量省时间版本 框的标注 共需要创建4个json标注文件 train.json, …

本地部署 Stable Diffusion XL Gradio Demo WebUI

StableDiffusion XL Gradio Demo WebUI 0. 先展示几张 StableDiffusion XL 生成的图片1. 什么是 Stable Diffusion XL Gradio Demo WebUI2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 安装 Stable Diffusion XL Gradio Demo WebUI6. 启动 Stable Diffusion XL Gradio De…

洛谷:P5016 龙虎斗 ← NOIP2018 普及组T2

【题目来源】https://www.luogu.com.cn/problem/P5016【题目描述】 轩轩和凯凯正在玩一款叫《龙虎斗》的游戏&#xff0c;游戏的棋盘是一条线段&#xff0c;线段上有 n 个兵营&#xff08;自左至右编号 1∼n&#xff09;&#xff0c;相邻编号的兵营之间相隔 1 厘米&#xff0c;…

Learning Spatial and Spatio-Temporal Pixel

Learning Deformable Kernels for Image and Video Denoising 作者&#xff1a; Xiangyu Xu 商汤科技SenseTime Research 论文思想&#xff1a;一是将传统的双边滤波算法与CNN结合起来&#xff0c;二是用变形卷积来做多帧对齐的问题&#xff0c;三还是在raw上进行处理的。 …

Zookeeper集群 + Kafka集群 + Filebeat + ELK

目录 一&#xff1a;Zookeeper 概述 1、Zookeeper 定义 2、Zookeeper 工作机制 3、Zookeeper 特点 4、 Zookeeper 数据结构 5、 Zookeeper 应用场景 6、 Zookeeper 选举机制 &#xff08;1&#xff09;第一次启动选举机制 &#xff08;2&#xff09;非第一次启动选举机制…

JMeter 如何模拟不同的网络速度

目录 前言&#xff1a; 限制输出带宽以模拟不同的网络速度 将这两行添加到user.properties文件中&#xff08;可以在JMeter安装的bin文件夹中找到此行&#xff09; 通过-J 命令行参数传递属性的值&#xff0c;如下所示&#xff1a; 前言&#xff1a; JMeter可以通过使用不同…

【mysql】—— 数据库的操作

序言&#xff1a; 在上篇文章我已经对数据库进行了详细的介绍&#xff0c;接下来我们就将上手学习操作的细节了。本篇文章便带领大家去学习有关库操作的基本知识&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;库的操作 1、 创建数据库 2、字符集和…

【深度学习笔记】训练 / 验证 / 测试集

本专栏是网易云课堂人工智能课程《神经网络与深度学习》的学习笔记&#xff0c;视频由网易云课堂与 deeplearning.ai 联合出品&#xff0c;主讲人是吴恩达 Andrew Ng 教授。感兴趣的网友可以观看网易云课堂的视频进行深入学习&#xff0c;视频的链接如下&#xff1a; 神经网络和…