🎯 进度条实现原理
1. 回车换行的关键区别
Plain
printf("\r倒计时: %2d", count); // \r 回车:回到行首不换行
printf("\n换行测试"); // \n 换行:移到下一行
重要区别:
-
\r(回车):光标回到当前行的开头,不换到新行 -
\n(换行):光标移动到下一行,位置不变 -
历史渊源:早期打字机时代,回车是让打字头回到行首,换行是转动滚筒到下一行
2. 缓冲区刷新机制
Plain
printf(LIGHT_GREEN "[%-100s][%d%%][%c]" NONE "\r", bar, rate, lable[rate % len]);
fflush(stdout); // 强制立即显示
为什么需要 fflush?
C语言输出默认使用缓冲区,内容不会立即显示到屏幕。fflush(stdout) 强制清空缓冲区,确保进度条实时更新。
缓冲区类型:
-
全缓冲:文件操作通常使用
-
行缓冲:终端输出,遇到换行符刷新
-
无缓冲:标准错误流 stderr
📊 进度条代码分析
头文件定义 (processBar.h)
Plain
#pragma once // 防止头文件重复包含
#include <stdio.h>
#include <unistd.h>
/*-------------进度条颜色-------------*/
#define NONE "\033[m"
#define LIGHT_GREEN "\033[1;32m"
#define LIGHT_RED "\033[1;31m"
#define LIGHT_BLUE "\033[1;34m"
#define LIGHT_YELLOW "\033[1;33m"
#define LIGHT_CYAN "\033[1;36m"
#define LIGHT_PURPLE "\033[1;35m"
#define LIGHT_WHITE "\033[1;37m"
/*-------------进度条样式------------*/
#define NUM 102 // 缓冲区大小
#define TOP 100 // 进度最大值
#define BODY '=' // 进度条主体
#define RIGHT '>' // 进度条头部
// 函数指针类型定义
typedef void (*callback_t)(int);
extern void InitBar(); // 初始化进度条
extern void ProcessBar(int rate); // 更新进度条
进度条核心实现 (processBar.c)
Plain
#include "processBar.h"
#include <string.h>
const char *lable = "|/-\\"; // 旋转指示器
char bar[NUM]; // 进度条缓冲区
// 初始化进度条
void InitBar()
{
memset(bar, '\0', sizeof(bar));
}
// 更新进度条显示
void ProcessBar(int rate)
{
if(rate < 0 || rate > 100) return;
int len = strlen(lable);
// 根据进度选择颜色
const char *color;
if(rate < 30)
color = LIGHT_RED;
else if(rate < 70)
color = LIGHT_YELLOW;
else
color = LIGHT_GREEN;
// 使用颜色和格式化输出
printf("%s[%-100s][%d%%][%c]" NONE "\r", color, bar, rate, lable[rate % len]);
fflush(stdout);
// 更新进度条内容
bar[rate++] = BODY;
if(rate < 100) bar[rate] = RIGHT;
}
下载模拟模块 (downLoad.h)
Plain
#pragma once
#include "processBar.h"
// 下载函数声明
void DownLoad(callback_t cb);
下载模拟模块 (downLoad.c)
Plain
#include "downLoad.h"
#include <unistd.h>
void DownLoad(callback_t cb)
{
int total = 1024; // 总大小 1024MB
int curr = 0; // 当前进度 0MB
while(curr <= total)
{
usleep(50000); // 模拟下载耗时 50ms
int rate = curr*100/total; // 计算进度百分比
cb(rate); // 回调函数更新进度条
curr += 8; // 每次下载8MB
}
printf("\n下载完成!\n"); // 下载完成换行
}
主程序 (main.c)
Plain
#include "downLoad.h"
#include <unistd.h>
int main()
{
printf("开始执行多个下载任务...\n\n");
printf(LIGHT_RED "任务一 下载中..." NONE "\n");
DownLoad(ProcessBar);
InitBar(); // 重置进度条
sleep(1); // 间隔1秒
printf(LIGHT_BLUE "任务二 下载中..." NONE "\n");
DownLoad(ProcessBar);
InitBar();
sleep(1);
printf(LIGHT_PURPLE "任务三 下载中..." NONE "\n");
DownLoad(ProcessBar);
InitBar();
printf(LIGHT_GREEN "所有任务执行完毕!" NONE "\n");
return 0;
}
🔧 Makefile 构建配置
Plain
CC = gcc
CFLAGS = -Wall -Wextra -std=c99
TARGET = processBar
SOURCES = processBar.c downLoad.c main.c
HEADERS = processBar.h downLoad.h
$(TARGET): $(SOURCES) $(HEADERS)
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET)
# 调试版本
debug: CFLAGS += -g -DDEBUG
debug: $(TARGET)
# 发布版本
release: CFLAGS += -O2
release: $(TARGET)
.PHONY: clean install uninstall
clean:
rm -f $(TARGET)
install: $(TARGET)
sudo cp $(TARGET) /usr/local/bin/
uninstall:
sudo rm -f /usr/local/bin/$(TARGET)
🎨 进度条设计亮点
1. 颜色系统
使用 ANSI 转义序列实现彩色输出:
-
\033[1;32m:亮绿色 -
\033[m:重置颜色 -
\033[1;31m:亮红色 -
\033[1;34m:亮蓝色 -
颜色编码原理 :
\033[是转义序列开始,1表示粗体,32表示绿色,m结束
2. 旋转指示器
Plain
const char *lable = "|/-\\"; // 旋转动画序列
lable[rate % len] // 循环显示旋转字符
动画原理:通过模运算循环显示四个字符,创建旋转效果
3. 回调函数设计
Plain
typedef void (*callback_t)(int); // 函数指针类型
void DownLoad(callback_t cb); // 接收回调函数
优势:
-
下载模块与显示模块解耦,便于复用和维护
-
支持不同的进度显示方式
-
符合开闭原则,易于扩展
4. 智能颜色切换
Plain
if(rate < 30)
color = LIGHT_RED; // 进度低显示红色
else if(rate < 70)
color = LIGHT_YELLOW; // 进度中显示黄色
else
color = LIGHT_GREEN; // 进度高显示绿色
🚀 编译和运行
编译项目
Plain
# 普通编译
make
# 调试版本
make debug
# 发布版本
make release
运行程序
Plain
./processBar
安装到系统
Plain
sudo make install
预期输出效果
Plain
开始执行多个下载任务...
任务一 下载中...
[=================>][75%][/]
下载完成!
任务二 下载中...
[=============================>][95%][-]
下载完成!
任务三 下载中...
[==================================================][100%][|]
下载完成!
所有任务执行完毕!
🔍 扩展功能建议
1. 添加时间估算
C
// 在 ProcessBar 函数中添加时间估算
static time_t start_time = 0;
if (start_time == 0) {
start_time = time(NULL);
}
time_t current_time = time(NULL);
int elapsed = (int)(current_time - start_time);
int remaining = (elapsed * (100 - rate)) / (rate + 1);
printf("[%ds/%ds]", elapsed, remaining);
2. 支持多种样式
C
// 进度条样式枚举
typedef enum {
STYLE_CLASSIC, // [====>]
STYLE_PERCENT, // 75%
STYLE_SPINNER, // ⣷
STYLE_BLOCK ███████
} ProgressStyle;
3. 网络下载集成
C
// 实际下载功能集成
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
// 更新下载进度
// 调用进度条回调
return size * nmemb;
}
📦 项目结构总结
Plain
progress_bar/
├── processBar.h # 头文件定义
├── processBar.c # 进度条核心实现
├── downLoad.h # 下载模块头文件
├── downLoad.c # 下载模拟实现
├── main.c # 主程序入口
└── Makefile # 构建配置