Colorful Printing
Posted on
May 8, 2018
平时在使用一些命令行工具时,是否经常注意到彩色的 log
打出来呢,然而自己写的命令行工具永远都是清一色白底黑字(或者黑底白字)的输出,是不是会怀疑别人施了黑魔法才让输出变得五彩斑斓,其实要让命令行输出彩色字体并没有那么难,关键就在对其规则的把握。
为了让字符变色,我们需要规定其伴随的属性,一般来说属性有以下几类。
基本属性,例如字体粗细,是否为斜体等等
前景字体颜色
前景字体颜色(强烈版)
背景字体颜色
背景字体颜色(强烈版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 const ( Reset Attribute = iota Bold Faint Italic Underline BlinkSlow BlinkRapid ReverseVideo Concealed CrossedOut ) const ( FgBlack Attribute = iota + 30 FgRed FgGreen FgYellow FgBlue FgMagenta FgCyan FgWhite ) const ( FgHiBlack Attribute = iota + 90 FgHiRed FgHiGreen FgHiYellow FgHiBlue FgHiMagenta FgHiCyan FgHiWhite ) const ( BgBlack Attribute = iota + 40 BgRed BgGreen BgYellow BgBlue BgMagenta BgCyan BgWhite ) const ( BgHiBlack Attribute = iota + 100 BgHiRed BgHiGreen BgHiYellow BgHiBlue BgHiMagenta BgHiCyan BgHiWhite )
让字符变色的关键在于转义字符的使用,转义字符对每个程序员来说肯定都不陌生,它的作用就是让紧随其后的字符变成其他含义,所谓转义转义就是这个意思,我们这里要使用的转义字符是 \x1b
,或者写作 \033
,二者都是可以的,因为十六进制的 1b
和八进制的 33
相等。
下面我们定义一个 Color
结构体,里面包含一个属性数组,规定了我们的文字会变化成什么样。
1 2 3 4 5 6 7 8 9 10 11 12 13 const escape = "\x1b" type Attribute int type Color struct { params []Attribute } func New (value ...Attribute) *Color { c := &Color{params: make ([]Attribute, 0 )} c.params = append (c.params, value...) return c }
例如一段文字 how are you?
,我们想要把把中间的 are
变为绿色,我们应该怎么办呢,其实就是分别在 are
的前后加上一些修饰符。
在 are
的前面加上 \x1b[32m
表示从这之后将后面的字变为绿色。 而在 are
的后面加上 \x1b[0m
则表示结束属性变化。
所以我们在开始变化的时候加上 fmt.Sprintf("%s[%sm", escape, c.sequence())
,这里的 escape
就是我们之前提过的转义字符,而 c.sequence()
就是我们规定的属性。
在变化结束的时候加上 fmt.Sprintf("%s[%dm", escape, Reset)
,这里 Reset
就表示重置变化,其值为0。
注意如果有多个属性,我们使用 ;
连接就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func (c *Color) sequence () string { format := make ([]string , len (c.params)) for i, v := range c.params { format[i] = strconv.Itoa(int (v)) } return strings.Join(format, ";" ) } func (c *Color) format () string { return fmt.Sprintf("%s[%sm" , escape, c.sequence()) } func (c *Color) unformat () string { return fmt.Sprintf("%s[%dm" , escape, Reset) } func (c *Color) wrap (s string ) string { return c.format() + s + c.unformat() }
下面我们对常见的几个函数作一层封装。
1 2 3 4 5 6 7 8 9 10 11 func (c *Color) Sprint (a ...interface {}) string { return c.wrap(fmt.Sprint(a...)) } func (c *Color) Sprintln (a ...interface {}) string { return c.wrap(fmt.Sprintln(a...)) } func (c *Color) Sprintf (format string , a ...interface {}) string { return c.wrap(fmt.Sprintf(format, a...)) }
使用 理清了基本思路,使用它就变的非常简单了。
1 2 3 4 func main () { c := New(Bold, FgGreen) fmt.Println(c.Sprintln("hello world" )) }