大家肯定都熟悉\n
\t
这一类转义字符,它们可以进行输出一些我们平时不太容易打出来的字符,与终端配合就可以做到一些特殊的操作。与之类似,\033
代表了ASCII
码为27
的字符ESC
。ESC
专门用于终端控制(所以被称作ansi escape code,ANSI转义字符),可以大量操作(在此不做展开)。
通常ESC
会与[
字符连接进行控制(当然还有其他许多),而ESC [
则被称为CSI(Control Sequence Introducer)。当然,由于CSI可以进行的操作也可多,本文仅就其控制文本样式这一点进行说明。
下文均以\033
表示ESC
(以及\033[
表示SCI),使用python
进行举例
让我们先看一段简单的例子:
容易总结出以下规律:
使用
\033[#m
来设置样式,其中#
代表具体的样式代码(一些数字),对于多样式使用;
将样式数字隔开。以3开头的两位数代表前景色,以4开头的两位数代表背景色;单独数字代表特殊样式(比如4代表下划线)
如上文所言,对于颜色有以下对应代码:
代码 | 颜色 |
---|---|
0 | 黑色 |
1 | 紅色 |
2 | 綠色 |
3 | 黃色 |
4 | 藍色 |
5 | 紫色 |
6 | 青色 |
7 | 白色 |
对于特殊样式,则有:
代码 | 意义 |
---|---|
0 | 默认 |
1 | 高亮 |
4 | 下划线 |
5 | 闪烁 |
7 | 反色 |
8 | 不可见 |
其实有more and more,但是懒得写了
对于上面的内容,一般的终端都支持(除了win家族比较老的终端以及idle一类的并非主要用于终端的东西)。其中有八种颜色,对应二进制需要3bit来标记。现在的大部分支持3bit的终端也支持4bit,即16种颜色。与3bit使用 3037 来表示前景色以及使用 4047 表示背景色类似,4bit在其基础上添加 9097 与 100107 来表示更亮的颜色。如下:
对于再新一些的终端,还支持8bit的256色,在linux中可以使用下面的命令测试自己的终端是否支持256色(只要正常显示了256种颜色就是支持):
(x=`tput op` y=`printf %76s`;for i in {0..256};do o=00$i;echo -e ${o:${#o}-3:3} `tput setaf $i;tput setab $i`${y// /=}$x;done)
对于256色的终端,使用\033[38;5;#m
与\033[48;5;#m
来表示前景色与背景色,#
可以是0~255的任意数,对照如下:
示例如下:
再新一些的终端甚至支持256bit的全彩色,再linux下可以使用下面的代码进行测试:
awk 'BEGIN{
s="/\\/\\/\\/\\/\\"; s=s s s s s s s s;
for (colnum = 0; colnum<77; colnum++) {
r = 255-(colnum*255/76);
g = (colnum*510/76);
b = (colnum*255/76);
if (g>255) g = 510-g;
printf "\033[48;2;%d;%d;%dm", r,g,b;
printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b;
printf "%s\033[0m", substr(s,colnum+1,1);
}
printf "\n";
}'
支持的将有以下输出:
对于全彩色,使用\033[38;2;r;g;bm
与\033[48;2;r;g;bm
进行设置。
综上所述,让我们写一个简单的printc()
函数来进行简便的带样式输出吧:
def printc(*args, fg: str | int | tuple[int] = '', bg: str | int | tuple[int] = '', light: bool = False, style: tuple[int | str] | str | int = (), sep=' ', end='\n', file=None, flush=False):
def prints(arg): return print(arg, end='', file=file, flush=flush)
def decoder(data):
if type(data) is str:
com_table = {'black': 0, 'red': 1, 'green': 2, 'yellow': 3,
'blue': 4, 'magenta': 5, 'cyan': 6, 'white': 7}
return com_table[data]
elif type(data) is int:
return f'8;5;{data}'
elif type(data) is tuple:
if len(data) == 3:
return f'8;2;{data[0]};{data[1]};{data[2]}'
if not light or type(fg) is not str or type(bg) is not str:
if fg:
prints(f"\033[3{decoder(fg)}m")
if bg:
prints(f"\033[4{decoder(bg)}m")
else:
if fg:
prints(f"\033[9{decoder(fg)}m")
if bg:
prints(f"\033[10{decoder(bg)}m")
if type(style) is tuple:
for s in style:
if type(s) is int:
prints(f'\033[{s}m')
elif type(s) is str:
com_table = {'reset': 0, 'bold': 1, 'faint': 2, 'italic': 3,
'underline': 4, 'invert': 7, 'hide': 8, 'delete': 9}
prints(f'\033[{com_table[s]}m')
elif type(style) is str:
com_table = {'reset': 0, 'bold': 1, 'faint': 2, 'italic': 3,
'underline': 4, 'invert': 7, 'hide': 8, 'delete': 9}
prints(f'\033[{com_table[style]}m')
elif type(style) is int:
prints(f'\033[{style}m')
print(*args, sep=sep, end=end, file=file, flush=flush)
prints('\033[0m')
写一下简单的测试:
if __name__ == '__main__':
printc('''Copyright(c) 2022 Expector
_ _
_ __ _ __(_)_ __ | |_ ___
| '_ \| '__| | '_ \| __/ __|
| |_) | | | | | | | || (__
| .__/|_| |_|_| |_|\__\___|
|_|''', fg=(0x66, 0xcc, 0xff), bg='black', style='bold')
input('Press enter to see the palette...')
print('\n4-bit 16-color')
for color in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']:
printc(f' {color} ', bg=color, end='')
print()
for color in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']:
printc(f' light {color} ', bg=color, light=True, end='')
print('\n8-bit 256-color')
for color in range(0, 256):
printc(f' {color} ', bg=color, end='')
print('\n24-bit true color')
for color in range(0, 77):
printc(' ', bg=(255-(color*255//76), color*510//76 if color <
38 else 510-color*510//76, color*255//76), end='')
跑起来!
源代码在这里
本文使用CC-BY-NC-ND进行许可