C语言写一个终端进度条

C语言写一个终端进度条

这个功能挺简单的,主要有以下两点:

  • 如何获取终端宽度
  • 如何让字符在原地闪烁

如何获取终端宽度

这里用到了设备控制接口函数ioctl(),下面简单的介绍一下这个函数的用法:

ioctl是一个在Unix和类Unix系统中用于设备驱动程序的设备特定操作的系统调用。它的名称代表“输入/输出控制”,但实际上它可以用于任何设备,不仅仅是输入/输出设备。

在C语言中,ioctl函数的原型如下¹:

#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...);

这里,fd是一个打开的文件描述符,cmd是设备特定的操作命令,后面的可变参数...取决于cmd指定的操作。

ioctl函数执行成功时返回0,失败则返回-1并设置全局变量errno值。可能的错误包括:

  • EBADF: fd不是一个有效的描述符。
  • EFAULT: argp引用了一个无法访问的内存区域。
  • EINVAL: 请求或argp无效。
  • ENOTTY: fd没有与字符特殊设备关联。
  • ENOTTY: 指定的请求不适用于描述符d引用的对象类型。

更详细的内容可以查询man手册或者其他博客!!这个函数还是挺复杂的!

对于判断终端大小的需求,我们有以下的使用方式:

	struct winsize w;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // 获取终端大小并且存储到w中
    int barWidth = w.ws_col - 10; // 减去10是为了放置越界

winsize这个结构体位于termios.h头文件内

// /usr/include/asm-generic/termios.h
Struct winsize 
{
    unsigned short ws_row;    /* rows, in character */
    unsigned short ws_col;        /* columns, in characters */
    unsigned short ws_xpixel;    /* horizontal size, pixels (unused) */
    unsigned short ws_ypixel;    /* vertical size, pixels (unused) */
}

使用这个结构体记录窗口终端大小。

如何让字符在原地闪烁

要明白这一点,我们首先要知道两个前置知识:

  • printf的输出条件
  • 终端中的光标

printf的输出条件

没错,printf 也有输出条件,如下:

  • 遇到换行符\n
  • 进程终止
  • 遇到fflush函数

单纯的使用printf并不会直接输出到终端上,会先放在缓冲区,只有遇到换行符才会输出!!!

终端中的光标

我们可以通过'\r'来控制缓冲区中的光标,这样,当我们输出进度条之后,光标又会回到进度条最一开始的位置,这样就可以使得进度条看不见了。

如下:

// 光标置于开头
// progressPercent是进程百分比
printf("\r%3d%% [", progressPercent);

整个函数

// 用法,输入进度值(小数)
// 此函数用于输出一次进度条
void printProgress(float progress) {
        struct winsize w;
        ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
        int barWidth = w.ws_col - 10; // -10 防止越界

        int progressPercent = (int) (progress * 100.0); // 计算出百分值
        printf("\r%3d%% [", progressPercent);
        int pos = barWidth * progress; // 包含隐式类型转换

        for (int i = 1; i < barWidth; ++i) {
                if (i < pos) {
                        printf("#");
                } else {
                        printf(" ");
                }
                fflush(stdout); // 输出完所有内容后刷新缓冲区,代码执行到这里才会有一次输出
        }
        printf("]"); // 最后补上后面的括号
}

这里再写一个main函数用于测试:

int main() {
    for (int i = 0; i <= 100; ++i) {
        printProgress(i / 100.0);
        sleep(1);
    }
    printf("\n");
    return 0;
}

因为作者在项目开发时使用的是自用的通用头文件,代码中并没有包含头文件,请读者自行添加。

执行效果

vscode执行效果:
执行效果1
ssh客户端执行效果:
执行效果2
读者可以给进度条加以改进,作为下层代码,方便在上层代码上调用(例如文件传输C/S)。

:wq 拜拜~~~~

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若亦_Royi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值