ZeroOne AI
← 返回文章列表

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

👁 30
分类:工业互联网

手把手用 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、智能制造与技术实践文章

工业AI

LlamaIndex 系列(三):上下文增强驱动的 LLM 应用开发框架

LlamaIndex 框架全景:核心组件、开源+商业双轮架构、5 行代码快速上手的文档问答实践,以及金融制造等行业的落地场景。

工业互联网

Linux系统篇(十九)——文件(三):底层存储入门:从机房硬件到 CHS/LBA 磁盘寻址全解析

从机房硬件层级讲到磁盘物理结构,再到 CHS 三维寻址与 LBA 线性寻址的转换原理,完整打通底层存储认知。

工业互联网

Linux系统篇(十三)——工具篇(五):取代命令行 GDB?CGDB 可视化调试全解析

CGDB 是 GDB 的轻量级终端可视化外壳:分屏源码与命令、Vim 快捷键、可视化断点,本文从安装、界面操作到实战配置全面讲解。

机器人自动化

TRIO-Basic 从入门到精通(八):QT使用Trio COM组件编程实战

本教程以实战方式演示QT环境下使用Trio COM组件(TrioPC)进行上位机编程的完整流程:安装TrioPCMotion组件、用dumpcpp导出描述头文件、在工程中添加axcontainer支持、创建TrioPC实例并通过GetAxisVariable等接口实时读取控制器各轴位置,快速搭建上位机监控界面。

MES

MES + 工业打印机:让每一件产品“自动生成身份证”,从生产到追溯真正闭环

在传统注塑工厂,MES记录生产、打印机打印标签,两套系统各自独立运行,人工重复录入容易出错。本文以实际工业场景为例,介绍如何构建 MES + 工业打印机自动打印网关:MES作为生产数据唯一来源,通过MQTT触发打印,打印网关自动拉取设备与生产数据并转换为打印协议,经TCP下发工业打印机完成产品标识,并把打印结果纳入追溯体系,真正实现“生产—打印—追溯”闭环。

基于Docker容器化部署工业智能体小龙虾

采用Docker容器化技术,将工业智能体"小龙虾"、数据库、Redis等核心服务进行模块化部署,结合工业智能网关实现边缘智能与云边协同,构建高可靠、易维护、可扩展的工业智能系统。

评论(0)