ZeroOne AI
← 返回文章列表

Linux系统篇(九)——C 语言实战:终端实时刷新进度条

👁 25
分类:工业互联网

手把手用 C 语言在 Linux 终端实现实时刷新进度条,讲透回车换行区别、行缓冲区与 fflush 强制刷新机制

项目背景

很多刚接触 Linux C 编程的同学,都会想做一个能在终端里"动起来"的程序——比如下载时那种从左到右填充、旁边还带旋转动画和百分比数字的进度条。这不仅是练习 C 语言基本功的好素材,更能在动手过程中把 \r(回车)、\n(换行)以及行缓冲区这些平时容易混淆的概念彻底搞懂。

本文从零开始,先讲清两个关键概念,再手写一个倒计时程序练手,最后给出完整可运行的进度条实现,帮助读者打通"终端字符控制"这条技能线。

技术方案

1. 回车 \r 与换行 \n 的区别

这两个符号的起源要追溯到老式打字机:

要在终端实现"原地刷新"效果,必须用 \r:它让光标回到行首,下一次打印的新内容就会覆盖上一次的内容,从而形成动态更新的视觉体验。如果误用 \n,进度条就会不断向下换行,变成"刷屏"而非"刷新"。

2. 行缓冲区的坑

在 C 语言中,printf 默认是行缓冲模式:只有当输出遇到 \n 换行符、程序正常结束,或者缓冲区被写满时,内容才会真正被送往终端。如果我们在循环里不断 printf 却不带换行、不刷新,屏幕上将什么都看不到。

解决办法有两个:

3. 先练手:倒计时程序

在实现进度条之前,先用一个 100 秒倒计时验证 \r + 刷新的组合思路:

#include <stdio.h>
#include <windows.h>  // 仅 Windows 演示用;Linux 下建议用 usleep()

int main()
{
    int time = 100;
    while (time >= 0)
    {
        // %-3d 左对齐占 3 位,避免位数变化时残留旧字符
        printf("%-3d\r", time);
        fflush(stdout);      // 强制刷新,立即显示
        Sleep(1000);         // 等待 1 秒
        time--;
    }
    printf("\n");
    return 0;
}

可以看到:\r 让数字始终在同一位置跳动,fflush 保证每次更新都能及时显示。

系统架构

进度条程序整体上是一个"刷新循环 + 状态映射"的结构,核心模块拆解如下:

模块职责实现要点
进度填充 buffer存放已完成的进度符号字符数组,按百分比填充 #
旋转动画 stat展示"正在工作"的指示器字符串 `"
进度映射 rate把当前量换算成百分比current / total 再乘 100
刷新输出覆盖上一帧画面\r 回行首 + fflush 强制上屏

实施过程

1. 头文件与接口声明

// proce.h
#ifndef __PROCE_H__
#define __PROCE_H__

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define N 101      // 进度条最大宽度(100 格 + 结束符)
#define STATE '#'  // 进度填充字符

void process();
void FlushProcess(double total, double current);

#endif

2. 核心刷新函数

// proce.c
#include "proce.h"

// 依据当前进度,刷新一次进度条显示
void FlushProcess(double total, double current)
{
    char buffer[N];                    // 进度填充缓冲区
    const char* stat = "|/-\\";        // 旋转动画字符集
    memset(buffer, 0, sizeof(buffer)); // 先清空

    int len = strlen(stat);            // 动画字符个数
    int num = (int)(current * 100 / total); // 已完成的格数

    // 填充进度条主体
    for (int i = 0; i <= num; i++)
    {
        buffer[i] = STATE;
    }

    double rate = current / total;     // 进度比例
    static int cnt = 0;                // 动画游标(静态变量跨调用保持)
    cnt %= len;

    // %-100s 左对齐占 100 格;%% 转义输出百分号
    printf("[%-100s][%d%%][%c]\r", buffer, (int)(rate * 100), stat[cnt]);
    cnt++;

    fflush(stdout);                    // 关键:立即刷新到终端
}

3. 主流程:模拟下载

int main()
{
    const double total = 100.0;   // 任务总量
    const double speed = 2.0;     // 每次推进的速度
    double current = 0;           // 当前完成量

    while (current <= total)
    {
        FlushProcess(total, current);
        usleep(3000);             // 暂停 3ms,模拟耗时操作
        current += speed;
    }

    printf("\n");
    printf("\ndownload %.2lfMB Done\n", current);
    return 0;
}

运行效果:进度条从左向右填充,右侧百分比同步增长,末尾的 | / - \ 旋转动画循环转动,全程在同一行内完成刷新,体验十分顺滑。

应用价值

SEO关键词

Linux, C语言, 进度条, 回车换行, 行缓冲区, fflush, 终端编程, printf

推荐阅读

更多工业 AI、智能制造与技术实践文章

机器人自动化

SOEM-EtherCAT 源代码解析(二):已知的从站配置信息列表

解析 SOEM 主站库的从站配置信息列表:ec_configlist_t 结构体字段含义、dtype 设备类型解释、倍福 EK1100/EL 系列端子预配置表,以及未知从站如何回退到 EEPROM 读取配置。

数码印刷

陶瓷喷墨 RIP 中的 8 色 ICC Profile 技术原理与 LittleCMS 实现

系统讲解陶瓷喷墨 RIP 中 8 色 ICC Profile 的技术原理与 LittleCMS 实现:ICC 色彩管理模型、8 通道颜色空间、Profile 制作流程(固定条件/色卡打印/烧成后测色/分光数据)、LittleCMS 集成与批量转换,以及墨量限制、通道线性化、喷头灰度映射与工业级软件架构设计。

机器人自动化

TRIO-Basic 从入门到精通(十二):Trio 实现 ModbusTCP 通讯

本教程演示两台Trio控制器间基于ModbusTCP的以太网通讯,主站通过MODBUS指令对从站保持寄存器进行读写,并实现快速超时、断线重连等可靠通讯机制,适用于与PLC、传感器等从站设备的数据交换。

机器人自动化

陶瓷盘UV喷墨印刷中的Z轴高度测量与防碰撞技术方案

陶瓷盘等异形产品 UV 喷墨印刷中,喷头与产品表面的高度控制是核心难点:距离太近会撞坏昂贵的 UV 喷头,距离太远则墨滴扩散、套色精度下降。本文系统讲解线扫相机与激光测高的分工、两级 Z 轴高度设计、浮雕跟随、三重防撞保护,并给出 3.8 米 10 盘位设备基于 Trio + EtherCAT 的完整架构与工作流程。

工业互联网

Linux系统篇(十八)——文件(二):吃透 Linux 重定向底层:从文件描述符到 VFS 缓冲全链路拆解

从文件描述符数组到 dup2 重定向,再到用户/内核双缓冲与 VFS 多态,完整拆解 Linux 输出重定向的内核实现链路。

机器人自动化

手把手做ROS2机器人系统开发(二):ROS2核心命令详解

在上一讲中,我们完成了 Ubuntu 22.04 与 ROS2 Humble 开发环境的搭建。本章节进入实战第一步:系统梳理 ROS2 的核心命令体系。熟练掌握这些命令,是后续编写节点、调试通信、排查问题的基本功。 ROS2 的命令统一以 `ros2` 开头,通过子命令分组管理:daemon、no

评论(0)