Linux 进度条实现:详解回车换行与缓冲区机制
Linux 下 C 语言进度条实现涉及回车换行符差异、标准输出缓冲区管理及刷新机制。文章通过倒计时示例演示光标回退技巧,随后提供基于回调函数的完整进度条代码,涵盖 Makefile 构建、ANSI 颜色控制及动态更新逻辑。

Linux 下 C 语言进度条实现涉及回车换行符差异、标准输出缓冲区管理及刷新机制。文章通过倒计时示例演示光标回退技巧,随后提供基于回调函数的完整进度条代码,涵盖 Makefile 构建、ANSI 颜色控制及动态更新逻辑。

回车和换行不是一码事。
这是一张普通的作文纸,每次我们写完一行,都要从下一行开头开始继续往下写,写完一段,都要新起一行——
如下图,键盘、打字机上面都有回车键——
为什么说回车和换行不是一回事呢?
是的,回车换行是两个动作,\r 是回车,\n 是换行(和\r\n是一样的),在 C/C++ 里面\n(C)、std::endl(C++)是把两个动作(回车和换行)合写成一个了。
[Alice@VM-4-17-centos Ludy]$ vim code.c
[Alice@VM-4-17-centos Ludy]$ make 我要开始编译了...
code.c -> code.o 我要开始链接了...
code.o -> code.exe
[Alice@VM-4-17-centos Ludy]$ ./code.exe hello world!
[Alice@VM-4-17-centos Ludy]$
这里就是字符位数不够,右对齐了——

fflush(stdout); // 强制刷新缓冲区
下面的代码会有哪些现象?
#include <stdio.h>
int main(){
printf("hello Alice!\n");
sleep(3);
return 0;
}
#include <stdio.h>
int main(){
printf("hello Alice!");
sleep(3);
return 0;
}
#include <stdio.h>
int main(){
printf("hello Alice!");
fflush(stdout);
sleep(3);
return 0;
}

#include <stdio.h>
#include <unistd.h>
int main()
{
int cnt = 10;
for(;cnt >= 0;cnt--)
{
printf("倒计时:%-.2d\r",cnt); // 格式化输出
fflush(stdout); // 强制刷新缓存
sleep(1);
}
printf("\n");
return 0;
}
[Alice@VM-4-17-centos Ludy]$ make
[Alice@VM-4-17-centos Ludy]$ ./code.exe
倒计时:0
进度条是用户界面中常见的元素,用于直观展示任务的完成进度。接下来介绍如何使用 C 语言在 Linux 操作系统中(centos 版本)实现一个功能完整的进度条。
Bin=process_bar
Cc=gcc
Src=$(wildcard *.c)
Obj=$(Src:.c=.o)
$(Bin):$(Obj)
@echo "$^ link to $@"
@$(Cc) -o $@ $^
%.o:%.c
@echo "compling $< to $@"
@$(Cc) -c $<
.PHONY:clean
clean:
@echo "Clean Project...Done"
@rm -f $(Obj)$(Bin)
.PHONY:Print
Print:
@echo $(Bin)
@echo $(Cc)
@echo $(Src)
@echo $(Obj)
定义函数指针类型
flush_t,支持回调机制。 声明进度条显示函数接口。
total - 总任务量:表示需要完成的全部工作量; current - 当前完成量:表示已经完成的工作量; speed - 当前速度:表示单位时间内完成的工作量; userinfo - 用户信息:这里表示速度单位:'MB/s'
#ifndef PROCESS_H
#define PROCESS_H
// ANSI 颜色代码
#define COLOR_RED "\033[31m"
#define COLOR_GREEN "\033[32m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_BLUE "\033[34m"
#define COLOR_MAGENTA "\033[35m"
#define COLOR_CYAN "\033[36m"
#define COLOR_RESET "\033[0m"
typedef void (*flush_t)(double total,double current,double speed,const char* userinfo);
void Process(double total,double current,double speed,const char* userinfo);
#endif
百分比计算: current * 100.0 / total进度条填充: 使用循环填充 - 字符旋转光标: 通过字符序列 |\-/ 实现动画效果实时刷新: 使用\r 回车符和 fflush(stdout) 实现原地更新#include "process.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// 如果头文件中没有定义颜色,则在这里定义
#ifndef COLOR_RED
#define COLOR_RED "\033[31m"
#define COLOR_GREEN "\033[32m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_BLUE "\033[34m"
#define COLOR_MAGENTA "\033[35m"
#define COLOR_CYAN "\033[36m"
#define COLOR_RESET "\033[0m"
#endif
#define SIZE 100
#define LABEL '='
void Process(double total, double current, double speed, const char* userinfo)
{
if(current > total)
return;
// 旋转光标
static const char* lable = "|/-\\";
static int index = 0;
int size = strlen(lable);
// 下载比率
double rate = current * 100.0 / total;
char out_bar[SIZE];
memset(out_bar, '\0', sizeof(out_bar));
// 填充进度字符
int i = 0;
int bar_length = (int)rate;
if(bar_length > 100) bar_length = 100;
for(; i < bar_length; i++)
{
out_bar[i] = LABEL;
}
// 选择颜色基于进度
const char* color;
if(rate < 30.0)
color = COLOR_RED;
else if(rate < 70.0)
color = COLOR_YELLOW;
else
color = COLOR_GREEN;
// 刷新进度条 - 使用 \r 确保不换行
printf("\r[%-100s] %5.1f%% [%c] %.1f/%.1f speed:%.1f %s",
out_bar, rate, lable[index], current, total, speed, userinfo);
fflush(stdout);
index++;
index %= size;
// 进度条完成,记得换行
if(current >= total)
{
printf("\n");
}
}
#include "process.h"
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
// 有一个下载任务
double gtotal = 1024.0; // 目标文件的总大小
double gspeed = 1.0; // 下载时的网络速度
// 回调函数
void Download(double total, flush_t cb)
{
double level[] = {0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 30.0, 30.0, 40.5, 80.5};
int num = sizeof(level) / sizeof(level[0]);
double current = 0.0; // 当前下载了多少
while(1)
{
usleep(100000); // 模拟下载,单位微秒
double speed = level[rand()%num]; // 随机网速
current += speed;
if(current >= total)
{
current = total;
cb(total,current,speed,"MB/s"); // 更新进度条
break;
}
else{
cb(total,current,speed,"MB/s"); // 更新进度条
}
}
}
int main()
{
srand(time(NULL));
// 多个不同大小程序的下载
printf("download:\n");
Download(gtotal,Process);
printf("download:\n");
Download(120.0,Process);
printf("download:\n");
Download(11.8,Process);
printf("download:\n");
Download(78.9,Process);
printf("download:\n");
Download(900.0,Process);
return 0;
}
初始化阶段 ↓ total = 1024.0(总任务量)
↓ 循环更新阶段 ↓ current = 0 → 逐渐增加 → total (当前进度)
speed = 随机值 (实时速度)
userinfo = "MB/s"(单位信息)
↓ 显示阶段 ↓
[----------][45.5%][\]|465.9/1024.0, speed:25.3MB/s

进度条(无色版本)


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online