0%

Rust实现进度条功能

本文用 Rust 语言实现一个简单的进度条功能,并介绍通过转义码在终端打印带颜色等格式的字符串。

一、代码实现

先上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pub fn bar_show() -> Result<(), Box<dyn Error>> {
const BAR_FRONT: &str = "-\\|/";
for index in 0..=100 {
print!(
"\r{} \u{1b}[42m{}\u{1b}[0m [ {}% ]",
BAR_FRONT.chars().nth(index % 4).unwrap(),
" ".repeat(index / 3),
index
);
stdout().flush()?;
sleep(Duration::from_millis(30));
}
println!();
Ok(())
}

因为终端输出的进度条是带颜色的,在这里没法显示,只能通过截图看。

进度条显示

二、代码解释

进度条前面的指示字符是 "-\\|/" 交替显示,呈现出动态效果,因为 \ 需要转义,所以是两个反斜杠 \\

然后就是进度条从 0-100 开始,每一次循环输出指示字符(模 4),空白字符重复 (index / times) 次,根据进度条长度进行调整除数。然后刷新输出流,要不然输出会在缓存中,每隔一段时间才会输出到设备,不会实时显示进度条进度,最后再睡眠 30ms,让进度条缓慢加载。

最重要的是这一句:"\r{} \u{1b}[42m{}\u{1b}[0m [ {}% ]"

首先,\r 表示将光标置于本行行首,使用 print! 可以使得每次输出覆盖之前输出的行。然后 \u 表示输出后面的 UniCode 字符,{} 就不用说了,是 Rust 里用来控制格式化输出的。

注:Unix 系统里,每行结尾只有“<换行>”,即“\n”;Windows 系统里面,每行结尾是“<换行><回车 >”,即“\n\r”;Mac 系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac 系统下的文件在 Windows里打开的话,所有文字会变成一行;而 Windows 里的文件在 Unix/Mac 下打开的话,在每行的结尾可能会多出一个 ^M 符号。所以,如果你需要跨平台处理文本文件,可能会被回车换行搅得有点头大。
0x0D(ascii 码是 13) 指的是“回车” \r 是把光标置于本行行首
0x0A(ascii 码是 10) 指的是“换行” \n 是把光标置于下一行的同一列
0x0D + 0x0A 回车换行 \r\n 是把光标置于下一行行首

\u{1b}[42m 这句的格式是 \x1b[<代码>;<代码>,其中 \x1b[ 是十六进制 1b,写成八进制 \033 也行,然后一个左中括号,是特殊的终端控制符,格式固定。然后后面跟上一个数字和一个字母,这里 42m 就是将背景设置为绿色,字母 m 表示设置的属性类别,数字代表属性值。

下面是一些其他属性,可以设置文本的颜色,背景色,设置是否加粗,下划线等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
\033[0m 关闭所有属性
\033[1m 设置加粗
\033[2m 设置模糊,有的终端可能不支持
\033[3m 设置斜体,有的终端可能不支持
\033[4m 下划线(单线)
\033[5m 闪烁(慢)
\033[5m 闪烁(快),有的终端可能不支持
\033[7m 交换背景色与前景色
\033[8m 隐藏所有
\033[30m 至 \033[37m 设置前景色
\033[40m 至 \033[47m 设置背景色
\033[nA 光标上移n行
\033[nB 光标下移n行
\033[nC 光标右移n行
\033[nD 光标左移n行
\033[y;xH 设置光标位置
\033[2J 清屏
\033[K 清除从光标到行尾的内容
\033[s 保存光标位置
\033[u 恢复光标位置
\033[?25l 隐藏光标
\033[?25h 显示光标

各个数字代表的颜色如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
字背景颜色范围:40--49
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色
字颜色: 30--39
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色

另外,同类的多种设置项可以组合在一起,中间用分号 ; 隔开。

例如 print!("\u{1b}[31;1;3;4m{}\u{1b}[0m", "Rosa");,其中 \u{1b}[31;1;3;4m 中,31 表示前景色(字的颜色)是红色,1 表示加粗,3 表示设置斜体,4 表示设置下划线。则上述代码输出的是一个红色加粗加下划线的斜体字符串 Rosa。最后的 \u{1b}[0m 表示将格式清除掉,否则下面输出的任何字符都将使用刚刚的样式。

同样,在 C 语言中也可以实现。

1
2
int color = 32;
printf("\033[20;1H\033[1;4;%dmHello, world.\033[0m", color);

这行命令首先 \033[20;1H 将光标移动到终端第 20 行第 1 列,之后的 \033[1;4;32m 将文本属性设置为高亮、带下划线且颜色为绿色,然后输出 Hello,world,最后 \033[0m 将终端属性恢复为默认值。

三、一些参考实现

3.1 bash 中输出带样式的字符

在 bash 中,通常我们可以使用 echo 命令加 -e 选项输出各种颜色的文本,echo -e 表示处理特殊字符,开启转义。

1
2
3
4
5
6
echo -e "\033[31mRed Text\033[0m"
echo -e "\033[32mGreen Text\033[0m"
echo -e "\033[33mYellow Text\033[0m"
echo -e "\033[34mBlue Text\033[0m"
echo -e "\033[35mMagenta Text\033[0m"
echo -e "\033[36mCyan Text\033[0m"

3.2 C 语言中输出颜色表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main(void)
{
int i, j, n;

for (i = 0; i < 11; i++) {
for (j = 0; j < 10; j++) {
n = 10 * i + j;
if (n > 108) break;
printf("\033[%dm %3d\033[m", n, n);
}
printf("\n");
}
return 0;
}

四、参考文档

浅析 \x1B[1;3;31mxterm.js\x1B[0m 是什么?如何在终端输出带颜色等格式的字符串

ANSI escape code

控制台终端输出颜色

Rust 官方有进度条实现的 indicatif crate,用法比较全面,这里是 源码解析