终端中的彩色文本:ANSI 字符颜色控制指令的格式构成简介

摘要

你知道终端上打印的彩色文本是怎样实现的吗?本文主要介绍控制台上关于彩色字符的格式构成规则,并列举一些简单的示例以供参考理解。

目录
[隐藏]

ANSI Escape Code是一种带内信令(In-band signaling)的转义序列标准,用于控制文本在终端上的光标位置、颜色和其他选项。在文本中嵌入确定的字节序列,大部分以 ESC 转义字符和 “[” 字符开始,终端会把这些字节序列解释为相应的指令,而不是普通的字符编码。

ANSI 转义序列中以 ESC [(即\x1b[) 开头的叫作 Control Sequence Introducer,简写为 CSI。终端会将 CSI 字节序列解释为具有相应含义的指令。以 CSI 开头的指令有很多,大致可分四类:光标移动指令、清屏指令、字符渲染(Graphic Rendition)指令和终端控制指令。

基于 CSI 控制指令可以实现的功能是非常丰富的。如果你使用过支持 telnet 的 BBS 论坛的话,应该会有所了解。例如,你可以在终端中执行如下命令,以 telnet 的方式访问水木社区体验一下其魅力:

telnet bbs.newsmth.net

1 ANSI colors 颜色设置控制符号

CSI 指令中有颜色相关的控制指令字符。按照表示颜色的形式和范围的不同,可将它们分为三类:

  • 3/4 位颜色表示法
  • 8 位颜色表示法
  • 24 位颜色表示法, 支持 RGB 色值

ANSI 转义字符序列的控制字符范围及含义,可以查阅维基百科了解详情: ANSI转义序列

npmjs 上有许多基于ANSI color styles指令封装的控制台颜色格式化工具包,如:

在这些工具库中,只有 chalk 实现了全部ANSI colors 颜色设置的接口封装支持,并将其作为独立 npm 包 ansi-styles 进行维护(chalk 5项目则将 ansi-styles 的主 js 文件进行了内置)。

2 3/4位颜色表示法

基本格式为: \x1b[< N>m[字符]\x1b[< M>m

其中 NM 分别表示控制字符和结束字符,具体为:

  • 前景色(彩色文本):\x1b[< N>m[字符]\x1b[39m。其中 M=39,N 取值范围 30<=n<=37
  • 背景色(彩色背景):\x1b[< N>m[字符]\x1b[49m。其中 M=49,N 取值范围 40<=n<=47

示例:

console.log('\x1b[92m\x1b[4m 那些听不见音乐的人以为跳舞的人疯了。\x1b[0m');
console.log('\x1b[32m 万事开头难,然后中间难,最后结尾难 \x1b[39m');

3/4位颜色表示法在各种终端类型中拥有最为广泛的支持,故常见的针对终端彩色文本格式化的工具库,基本均基于此语法规则而封装。

3 8位颜色表示法

基本格式为: \x1b[< 38|48>;5;< N>m[字符]\x1b[0m

其中38表示选择前景色,48表示选择背景色,N 取值范围0~255,可表示 256 种颜色。末尾的\x1b[0m表示重置设置。

示例:

console.log('\x1b[38;5;129m最近手头有点紧,想借你的手牵一牵\x1b[0m');

ANSI colors 8位表示法全部前景色示例:

function printColors256() {
  const colorList = [''];
  const bgcolorList = [''];
  let idx = -1;

  while(idx++ < 255) {
      const text = String(idx).padEnd(3, ' ');
      const br = !idx || (idx + 1) % 16 ? '' : '\n';
      colorList.push(`\x1b[38;5;${idx}m${text}\x1b[0m ${br}`);
      bgcolorList.push(`\x1b[48;5;${idx}m${text}\x1b[0m ${br}`);
  }
  console.log(`ANSI 256 colors:\n${ colorList.join('') }`);
  console.log(`ANSI 256 background colors:\n${bgcolorList.join('')}`);
}
printColors256();

ANSI colors 256 示例:

function rainbow(words, startPos = Math.ceil(Math.random() * 100)) {
  // const colorCodes = new Array(256).fill().map(() => Math.floor(Math.random() * 255));
  const colorCodes = [196, 214, 45, 128, 226, 207, 42, 205, 112, 39, 122, 227, 111];
  const str = words
    .split(' ')
    .map(word =>
      word
        .split('')
        .map((ch, idx) => `\x1b[38;5;${colorCodes[(idx + startPos) % colorCodes.length]}m${ch}\x1b[0m`)
        .join('')
    )
    .join(' ');
  console.log(str);
}

rainbow('Her wishful face haunts my dreams like the rain at night.');

4 24位颜色表示法

基本格式为: \x1b[< 38|48>;2;< R>;< G>;< B>m[字符]\x1b[0m

其中38表示选择前景色,48表示选择背景色。R G B 表示三原色,取值范围分别都是 0~255

示例:

console.log('\x1b[38;2;25;154;48m透过你的眼 \x1b[38;2;255;154;48m离别也是诗 \x1b[0m');
console.log('\x1b[48;2;48;154;255m \x1b[38;2;255;255;255m 偶尔还是需要出去走一走,才知道躺在床上多么舒服 \x1b[0m');

RGB 模式可以有 1600+ 万种颜色组合,可以支持的色彩相当丰富了。但并不是所有的终端都支持该模式。

值得一提的是,从 Chrome 69 开始,其浏览器控制台增加了 ANSI Escape colors 的编码指令支持。经过测试,其控制台支持 3/4位表示法和24位表示法的完整格式。你可以复制上面的相关例子,在 Chrome Console 控制台测试体验。

5 扩展参考

关于 ANSI Escape Code 相关基础知识的细节,如有兴趣可参阅以下链接中的文档或文章进一步了解:

点赞 (0)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code