MeteorCat / 字体变色终端

Created Fri, 29 Sep 2023 17:56:10 +0800 Modified Wed, 29 Oct 2025 23:25:05 +0800
618 Words

字体变色终端

常见于命令行终端会输出变色的字体,这种都是采用 \033[31m 起始特殊字符加上 \033[0m 回滚颜色指令来做字体变色处理, 常见于类似 Python 这样来处理终端颜色字体:

GlobalColors = {
    'red':      '\033[31m',
    'blue':     '\033[94m',
    'green':    '\033[92m',
    'yellow':   '\033[93m',
    'magenta':  '\033[35m',
    'cyan':     '\033[36m'
}

""" 字体变色 """
def colored(strg, color):
    col = GlobalColors.get(color, None)
    if col:
        return col+str(strg)+'\033[0m'
    return strg

""" 调用打印 """
if __name__ == '__main__':
    print("%s %%\r" % (colored('error!', 'red')), end="")

这样可以看到终端颜色字体输出不是常规的红色字体, 这种就是利用终端特殊字符来做的变色切换,其他语言基本上通用。

Rust

如果简单在 Rust 使用会发现输出异常:

// 字体变色处理, 异常
println!("\033[31m{}\033[0m","hello.world");

实际上这是因为 Rust 默认采用 utf8 编码处理, ‘\033’ 这个特殊字符直接输出成原始字符, 所以需要将其字符手动转化成 \x1b 处理.

// 字体变色处理, 正常变色
println!("\x1b[31m{}\x1b[0m","hello.world");

以这个方法做基础可以封装成自己常用的工具类库:

/// 终端颜色
enum ColorStr<'a> {
    Red(&'a str),
    Green(&'a str),
    Yellow(&'a str),
    Blue(&'a str),
}

/// 调用 into 转化成 string 的处理
impl<'a> From<&ColorStr<'a>> for String {
    fn from(value: &ColorStr<'a>) -> Self {
        match value {
            ColorStr::Red(msg) => format!("\x1b[31m{}\x1b[0m", msg),
            ColorStr::Green(msg) => format!("\x1b[92m{}\x1b[0m", msg),
            ColorStr::Yellow(msg) => format!("\x1b[93m{}\x1b[0m", msg),
            ColorStr::Blue(msg) => format!("\x1b[94m{}\x1b[0m", msg),
        }
    }
}


/// 调用打印的时候的输出转化
impl<'a> Display for ColorStr<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let msg: String = self.into();
        write!(f, "{}", msg)
    }
}


fn main() {
    println!("{}", ColorStr::Red("error: this is red"));
    println!("{}", ColorStr::Yellow("warn: this is yellow"));
    println!("{}", ColorStr::Green("success: this is green"));
    println!("{}", ColorStr::Blue("info: this is blue"));
    println!("End");
}

执行之后就能看到五颜六色的终端字体的变色了, 最终封装成 printer.rs 库:

use std::fmt::{Debug, Display, Formatter};


/// cmd font color
///
/// ```rust
/// use tools::Print;
/// println!("{}",Print::Green("hello.world[success]"));
/// println!("{}",Print::Red("hello.world[red]"))
/// ```
#[allow(dead_code)]
pub enum Print<'a> {
    Black(&'a str),
    DarkGray(&'a str),
    Blue(&'a str),
    LightBlue(&'a str),
    Green(&'a str),
    LightGreen(&'a str),
    Cyan(&'a str),
    LightCyan(&'a str),
    Red(&'a str),
    LightRed(&'a str),
    Purple(&'a str),
    LightPurple(&'a str),
    Yellow(&'a str),
    LightYellow(&'a str),
    LightGray(&'a str),
    White(&'a str),
}


impl<'a> Display for Print<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Black(s) => write!(f, "\x1b[0;30m{}\x1b[0m", s),
            Self::DarkGray(s) => write!(f, "\x1b[1;30m{}\x1b[0m", s),
            Self::Blue(s) => write!(f, "\x1b[0;34m{}\x1b[0m", s),
            Self::LightBlue(s) => write!(f, "\x1b[1;34m{}\x1b[0m", s),
            Self::Green(s) => write!(f, "\x1b[0;32m{}\x1b[0m", s),
            Self::LightGreen(s) => write!(f, "\x1b[1;32m{}\x1b[0m", s),
            Self::Cyan(s) => write!(f, "\x1b[0;36m{}\x1b[0m", s),
            Self::LightCyan(s) => write!(f, "\x1b[1;36m{}\x1b[0m", s),
            Self::Red(s) => write!(f, "\x1b[0;31m{}\x1b[0m", s),
            Self::LightRed(s) => write!(f, "\x1b[1;31m{}\x1b[0m", s),
            Self::Purple(s) => write!(f, "\x1b[0;35m{}\x1b[0m", s),
            Self::LightPurple(s) => write!(f, "\x1b[1;35m{}\x1b[0m", s),
            Self::Yellow(s) => write!(f, "\x1b[0;33m{}\x1b[0m", s),
            Self::LightYellow(s) => write!(f, "\x1b[1;33m{}\x1b[0m", s),
            Self::LightGray(s) => write!(f, "\x1b[0;37m{}\x1b[0m", s),
            Self::White(s) => write!(f, "\x1b[1;37m{}\x1b[0m", s),
        }
    }
}

impl<'a> Debug for Print<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f,"{}",self)
    }
}