【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 观察现象下面我们还是来观察两个程序先看第一个这里用了一个函数sleepsleep() 函数用于在程序中暂停执行一段时间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转义序列是一系列的字符组合用于控制终端的文本样式和颜色。关于配色方案网上可以找到很多相关的资料大家有兴趣可以按照自己的喜好去配色我这里简单演示一下比如我配个红色来个绿色还有背景颜色也可以设置试一下来个青色也可以同时设置字体颜色/前景颜色和背景颜色我们来个字体红色背景青色大家可以按自己的喜好设置。