怎么提升网站收录,全国设计师网站,wordpress 获取用户id,旅行社网站建设规划的内容进度条---命令行版本
回车换行
其实本质上回车和换行是不同概念#xff0c;我们用一张图来简单的理解一下#xff1a; 在计算机语言当中#xff1a;
换行符#xff1a;\n
回车符#xff1a;\r
\r\n#xff1a;回车换行 这时候有人可能会有疑问#xff1a;我在学习C…进度条---命令行版本
回车换行
其实本质上回车和换行是不同概念我们用一张图来简单的理解一下 在计算机语言当中
换行符\n
回车符\r
\r\n回车换行 这时候有人可能会有疑问我在学习C/C语言的时候单纯的\n就起到了回车加换行的行为呀?! 答因为在语言层面\n给我们编译成了\r\n 缓冲区问题
在我们目前的阶段我们将缓冲区看作是一段内存块我们可以先实现一个测试代码
#touch test.c#可以直接
vim test.c
测试代码
技巧补充我们可以在底行模式使用man手册查看sleep(3)函数所需的头文件 :!man 3 sleep #includestdio.h
#includeunistd.hint main()
{
//printf(hello Linux!\n);printf(hello Linux!);sleep(3);return 0;
}我们在Linux编译后./test执行时当只有上被注释的代码被执行和只有第二句printf的代码我们发现前者内容打印出来程序3秒后结束后者是3秒后程序结束才打印内容我们可以明显感受到程序是先执行sleep(3)的但是我们之前学习C/C知道我们定义的程序逻辑都是从上往下执行的可是我们为什么没有看到字符串呢
我们应该知道对于后者printf在sleep之前一定执行完了但是显示器上没有显示那么在我们休眠3秒期间字符串 hello Linux! 在哪里其实该字符串在缓冲区里面可以理解为内存有一小内存块将该字符串临时保存在该处了。我们带有\n的字符串可以在显示屏上打印我们称为行刷新也就是遇到\nprintf执行完就默认将其字符串直接显示到显示器了行刷新可以理解为没有\n就不给行刷新
那后者为什么最后也可以刷新出来呢
是因为程序退出了会自动刷新缓冲区 我们如何可以让不带\n的字符串能够立马刷新呢
我们如果想让不带\n的字符串立马刷新可以使用fflush
fflush 是 C 语言标准库函数之一用于刷新流的缓冲区。其原型定义在 stdio.h 头文件中
我们可以进行man手册查看。
printf(hello Linux!);
fflush(stdout);
其实我们的程序会默认打开三个输入输出流分别是描述符分别为123 标准输入流stdin用于从标准输入设备通常是键盘读取数据。 标准输出流stdout用于向标准输出设备通常是屏幕输出数据。 标准错误流stderr用于输出错误信息和其他诊断信息通常也是输出到屏幕。
都是一个指向 FILE 结构体的指针定义在 stdio.h 中。
我们的printf默认是从标准输出流里面打我们可以看到fprintf其实就是比printf多了一个参数 在Linux下一切皆文件当一个程序在 Linux 下运行时操作系统会自动为该程序打开三个标准流文件描述符也就是下面两条代码是等价的fprintf只是显示的表示出往哪里输出 fprintf(stdout,hello Linux!\n);printf(hello Linux!\n);我们就可以利用缓冲区来实现一个倒计时光标显示后回退回车
int main()
{int i9;while(i0){printf(%d\r,i);i--;sleep(1);}return 0;
}但是我们发现在显示器上没有显示任何数据这是为什么呢
因为我们行刷新是需要\n的%d后是\r\r不支持我们行刷新所以对应的信息并未有显示出来还在缓冲区里面存着呢所以需要用到fflush(stdout);
int main()
{int i9;while(i0){printf(%d\r,i);fflush(stdout);i--;sleep(1);}printf(\n);//跑完之后想要保留0命令行不让会覆盖return 0;
}为了使我们对下面进度条程序更好的实现我们来看看当i取10的时候 命令行变化10-90-80-70-60-50-40-30-20-10-00----00 这时候我们应该好好理解显示 举个例子我们往显示器上输入:12345是输出的是12345数字还是1 2 3 4 5字符
显示器只认识字符显示器是字符设备所以输出的是后者这也是为什么我们需要像%d的格式化输出所以我们可以将上述代码中的%d改成%2d还可以在前面加一个-即%-2d来整体靠左显示代码我就不写出来了在原代码上做改进就。
以上我们进度条的预备工作就基本完成了回车换行缓冲区问题格式问题字符设备的理解问题输出设备的刷新问题
我们想写一个怎么样的进度条
框架的搭建
创建文件
首先在我们自己的工作目录中创建四个文件 main.c Makefile process.c process.h
编写 Makefile
使用 vim 编辑器打开 Makefile并写入以下内容
SRC:$(wildcard *.c)
OBJ:$(SRC:.c.o)
BINprocessbar$(BIN): $(OBJ)gcc -o $ $^%.o: %.cgcc -c $.PHONY: clean
clean:rm -f $(OBJ) $(BIN)
这个 Makefile 定义了如何编译和链接你的程序。它使用 wildcard 自动查找所有 .c 文件并生成相应的 .o 文件。最终生成的可执行文件名为 processbar。
编写 process.h
使用 vim 编辑器打开 process.h并写入以下内容
#pragma once
#include stdio.hvoid process_v1();
这个头文件声明了一个函数 process_v1该函数将在 process.c 中实现。
编写 process.c
使用 vim 编辑器打开 process.c并写入以下内容
#include process.hvoid process_v1() {printf(hello rose!\n);
}
这个文件实现了 process_v1 函数目前只是简单地打印一条消息。
编写 main.c
使用 vim 编辑器打开 main.c并写入以下内容
#include process.hint main() {process_v1();return 0;
}
这个文件是程序的入口点它调用了 process_v1 函数。
编译和运行
完成上述文件编写后你可以使用以下命令进行编译
make
这将根据 Makefile 的规则编译并生成 processbar 可执行文件。然后你可以运行该程序
./processbar
如果一切顺利你应该会看到输出 hello rose!。
清理
最后使用以下命令清理生成的文件
make clean
这将删除所有 .o 文件和 processbar 可执行文件。
接下来我们就可以在 process.c 中实现进度条的功能。可以根据具体需求设计进度条的更新逻辑和显示方式。
version1原理版本
#includeprocess.h
#includestring.h
#includeunistd.h#define NUM 101//因为会有一个\0的存在
#define STYLE void process_v1()
{ char buffer[NUM];memset(buffer,0,sizeof(buffer));const char *lable|/-\\;size_t lenstrlen(lable);//计数器int cnt0;while(cnt100)//这个循环会循环101次{printf([%-100s][%d%%][%c]\r,buffer,cnt,lable[cnt%len]);fflush(stdout);buffer[cnt]STYLE;cnt;//sleep(1);usleep(10000);}printf(\n);return;
} 定义常量和变量 NUM 定义为 101因为进度条的长度为 100 个字符加上一个字符串的终止符 \0。STYLE 定义为 用于表示进度条的已填充部分。buffer 是一个字符数组用于存储进度条的当前状态。lable 是一个字符串包含旋转的符号 |/-\用于在进度条旁边显示一个旋转的动画效果。len 是 lable 字符串的长度。cnt 是一个计数器用于控制进度条的更新和旋转符号的切换。 初始化缓冲区 使用 memset 函数将 buffer 初始化为全 0即空字符串。 进度条更新循环 while 循环会执行 101 次因为 cnt 从 0 开始直到 100 结束。在每次循环中使用 printf 函数输出当前的进度条状态 [%-100s] 表示一个宽度为 100 的左对齐字符串buffer 作为参数传入表示进度条的当前填充状态。[%d%%] 表示当前的百分比进度cnt 作为参数传入。[%c] 表示旋转符号lable[cnt%len] 计算当前应该显示的旋转符号。使用 \r 作为 printf 的结尾表示返回到行首这样下一次输出会覆盖当前行的内容。调用 fflush(stdout) 确保输出立即显示在屏幕上而不是被缓冲。将 buffer[cnt] 设置为 STYLE即 表示进度条的已填充部分向右扩展一个字符。cnt 更新计数器。usleep(10000) 暂停 10 毫秒使进度条的更新速度适中便于观察。注释掉的 sleep(1) 是暂停 1 秒的另一种方式但会使进度条更新过慢。 结束输出 循环结束后调用 printf(\n) 输出一个换行符使光标移动到下一行避免后续输出覆盖进度条。
整体功能
这个 process_v1 函数实现了一个动态更新的文本进度条进度条的长度为 100 个字符旁边有一个旋转的符号动画。通过在循环中逐步填充 buffer 并输出模拟了进度条的动态增长过程。每次更新后使用 \r 返回行首并刷新输出实现了进度条的原地更新效果。
version2真实版本
main.c
#include process.h#include stdio.h
#include unistd.hdouble total 1024.0; // 总下载量单位为MB
double speed 1.0; // 下载速度单位为MB/svoid DownLoad() {double current 0; // 当前已下载量while (current total) {// 刷新进度条FlushProcess(total, current);// 模拟下载数据每次循环下载speed大小的数据usleep(300000); // 暂停300ms模拟下载延时current speed;}printf(\ndownload %.2lfMB Done\n, current);
}int main() {DownLoad();return 0;
}
version2
#include process.h
#include string.h
#include unistd.h#define NUM 101 // 因为会有一个\0的存在
#define STYLE // version2
void FlushProcess(double total, double current) {char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char *lable |/-\\;size_t len strlen(lable);static int cnt 0; // 静态变量用于记录旋转符号的位置// 计算当前进度条的填充数量int num (int)(current * 100 / total);for (int i 0; i num; i) {buffer[i] STYLE;}double rate current / total; // 计算当前下载进度的百分比cnt % len; // 计算旋转符号的索引printf([%-100s][%.1f%%][%c]\r, buffer, rate * 100, lable[cnt]);cnt;fflush(stdout); // 确保进度条立即显示
}
进度条与下载模拟功能梳理 下载模拟 DownLoad 函数模拟了一个下载过程其中 total 表示总下载量speed 表示下载速度。在 while 循环中每次循环模拟下载 speed 大小的数据并通过 usleep 函数暂停一段时间来模拟下载延时。循环过程中不断调用 FlushProcess 函数来刷新进度条显示当前的下载进度。 进度条刷新 FlushProcess 函数负责根据当前下载量 current 和总下载量 total 计算进度条的填充状态并输出到屏幕。buffer 数组用于存储进度条的当前状态num 变量计算出需要填充的 字符的数量。使用 printf 函数输出进度条其中 [%-100s] 表示一个宽度为 100 的左对齐字符串用于显示进度条的填充部分[%.1f%%] 显示当前的下载百分比[%c] 显示一个旋转的符号用于增加动画效果。使用 \r 作为 printf 的结尾表示返回到行首使下一次输出覆盖当前行的内容实现进度条的原地更新效果。fflush(stdout) 确保进度条的输出立即显示在屏幕上而不是被缓冲。
整体功能
这个程序通过模拟下载过程并在每次下载数据后刷新进度条实现了动态显示下载进度的效果。进度条的长度为 100 个字符旁边有一个旋转的符号动画使用户可以直观地看到下载进度的变化。当下载完成后程序会输出一条完成信息告知用户下载已成功完成。
我们不仅仅可以在下载方面使用进度条我们也可以在上传方面使用进度条因此在main.c源文件当中我们typedef一个函数指针来使用回调函数来优化代码
#includeprocess.h#includestdio.h
#includeunistd.htypedef void (*callback_t) (double total,double current);//函数指针double total1024.0;
double speed1.0;//回调函数
void DownLoad(callback_t cb)
{double current0;while(currenttotal){//刷新进度cb(total,current);//下载代码usleep(3000);//充当下载数据currentspeed;}printf(\ndownload %.2lfMB Done\n,current);
}void UpLoad(callback_t cb)
{double current0;while(currenttotal){//刷新进度cb(total,current);//下载代码usleep(3000);//充当下载数据currentspeed;}printf(\nUpload %.2lfMB Done\n,current);
}
int main()
{DownLoad(FlushProcess);UpLoad(FlushProcess);return 0;
}