通过jansi实现控制台彩色输出

音乐推荐

说明

  本文旨在介绍个人对jansi的封装处理(具体的jansi使用教程请自行Baidu或者Google),jansi包用于在控制台中输出色彩丰富的文字,其Maven坐标为:

1
2
3
4
5
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.13</version>
</dependency>

  Intellij IDEA默认支持Ansi Color,即可以正常识别Ansi字符对应的颜色,但不支持斜体、双下划线、闪烁等特效;而Eclipse控制台默认无法识别Ansi颜色字符,它需要通过安装一个插件来实现,插件的地址为:http://www.mihai-nita.net/eclipse,安装方式可以参考:Eclipse安装AnsiInConsole插件 中的Installation说明(实际上Eclipse安装了该插件仅仅是支持了Ansi颜色字符的识别,依然不支持特效)。

彩色文字输出简单封装

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
62
63
64
65
66
67
68
package me.junbin.commons.ansi;
import org.fusesource.jansi.Ansi;
import static org.fusesource.jansi.Ansi.Color.*;
/**
* @author : Zhong Junbin
* @email : <a href="mailto:rekadowney@163.com">发送邮件</a>
* @createDate : 2017/1/24 10:02
* @description :
*/
public final class ColorfulPrinter {
private ColorfulPrinter() {
throw new AssertionError("No instances of ColorfulPrinter for you!");
}
public static String fgString(final Ansi.Color color, final Object object) {
return Ansi.ansi().eraseScreen().fgBright(color).a(object).reset().toString();
}
public static String bgString(final Ansi.Color color, final Object object) {
return Ansi.ansi().eraseScreen().bgBright(color).a(object).reset().toString();
}
public static String fgRedString(final Object object) {
return fgString(RED, object);
}
public static String bgRedString(final Object object) {
return bgString(RED, object);
}
public static String fgGreenString(final Object object) {
return fgString(GREEN, object);
}
public static String bgGreenString(final Object object) {
return bgString(GREEN, object);
}
public static void red(final Object object) {
System.out.println(fgRedString(object));
}
public static void fgRed(final Object object) {
red(object);
}
public static void bgRed(final Object object) {
System.out.println(bgRedString(object));
}
public static void green(final Object object) {
System.out.println(fgGreenString(object));
}
public static void fgGreen(final Object object) {
green(object);
}
public static void bgGreen(final Object object) {
System.out.println(bgGreenString(object));
}
// 由于其他颜色的处理过程类似,因此这里省略了
}

彩色文字输出测试

  测试样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import static me.junbin.commons.ansi.ColorfulPrinter.*; // 静态导入
@Test
public void test01() throws Exception {
white("前景色渲染");
red("Red");
green("Green");
yellow("Yellow");
magenta("Magenta");
blue("Blue");
cyan("Cyan");
System.out.println(negativeString("背景色渲染"));
bgRed("Red");
bgGreen("Green");
bgYellow("Yellow");
bgMagenta("Magenta");
bgBlue("Blue");
bgCyan("Cyan");
System.out.println("其他特效");
negative("反显[前景色和背景色互换]");
bold("黑体");
underline("下划线");
}

  测试结果:

控制台彩色输出基础测试.jpg

  本测试是基于Intellij IDEA的测试,对于Eclipse可能部分特效不支持。

高度可定制化的控制台彩色输出

  除了一个简单的输出封装之外,本人也封装了一个高度可定制化的有趣的输出工具ColorfulRender,该类以链式编程的方式来进行中间渲染处理,同时通过terminated字段实现每个实例只能调用一次终止方法:

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
public final class ColorfulRender {
private final Ansi ansi;
/**
* 是否开启亮色调,默认开启
*/
private boolean bright = true;
/**
* 是否开启前景色渲染,默认开启
*/
private boolean fg = true;
/**
* 是否开启背景色渲染,默认关闭
*/
private boolean bg = false;
/**
* 前景色配置
*/
private Ansi.Color fgColor = Ansi.Color.DEFAULT;
/**
* 背景色配置
*/
private Ansi.Color bgColor = Ansi.Color.DEFAULT;
/**
* 是否开启反显,默认关闭
*/
private boolean negative = false;
/**
* 是否开启下划线,默认关闭
*/
private boolean underline = false;
/**
* 是否开启黑体,默认关闭
*/
private boolean bold = false;
/**
* 是否已终止
*/
private boolean terminated = false;
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@ 初始化操作 @@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// 通过 new 关键字或者直接通过 custom、start 静态方法实例化渲染器
public ColorfulRender() {
this.ansi = Ansi.ansi().eraseScreen();
}
public static ColorfulRender custom() {
return new ColorfulRender();
}
public static ColorfulRender start() {
return new ColorfulRender();
}
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@ 中间操作 @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/**
* 色调渲染切换
*
* @param bright {@code true} 表示切换为亮色调,{@code false} 表示切换为暗色调
*/
public ColorfulRender bright(boolean bright) {
this.bright = bright;
return this;
}
/**
* 切换为亮色调渲染
*/
public ColorfulRender bright() {
this.bright = true;
return this;
}
/**
* 切换为亮色调渲染
*/
public ColorfulRender brightOn() {
this.bright = true;
return this;
}
/**
* 切换为暗色调渲染
*/
public ColorfulRender brightOff() {
this.bright = false;
return this;
}
private void offStyle() {
this.ansi.a(Ansi.Attribute.UNDERLINE_OFF);
this.ansi.a(Ansi.Attribute.INTENSITY_BOLD_OFF);
this.ansi.a(Ansi.Attribute.NEGATIVE_OFF);
}
/**
* 样式渲染
*/
private void renderStyle() {
offStyle();
if (this.underline) {
this.ansi.a(Ansi.Attribute.UNDERLINE);
}
if (this.bold) {
this.ansi.a(Ansi.Attribute.INTENSITY_BOLD);
}
if (this.negative) {
this.ansi.a(Ansi.Attribute.NEGATIVE_ON);
}
}
/**
* 色彩渲染
*/
private void renderColor() {
if (bright) {
if (this.fg) {
this.ansi.fgBright(fgColor);
} else {
this.ansi.fgBrightDefault();
}
if (this.bg) {
this.ansi.bgBright(bgColor);
} else {
this.ansi.bgBrightDefault();
}
} else {
if (this.fg) {
this.ansi.fg(fgColor);
} else {
this.ansi.fgDefault();
}
if (this.bg) {
this.ansi.bg(bgColor);
} else {
this.ansi.bgDefault();
}
}
}
/**
* 内容渲染
*
* @param object 要渲染的内容
*/
public ColorfulRender render(Object object) {
if (terminated) {
throw new IllegalStateException("Render operation has already been terminated!");
}
this.renderStyle();
this.renderColor();
this.ansi.a(object);
return this;
}
/**
* 内容渲染
*
* @param object 要渲染的内容
*/
public ColorfulRender andThen(Object object) {
return render(object);
}
/**
* 插入新行
*/
public ColorfulRender newLine() {
this.ansi.newline();
return this;
}
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@ 终止操作 @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/**
* 结束当前的渲染
*/
public Ansi end() {
if (this.terminated) {
throw new IllegalStateException("Render operation has already been terminated!");
}
this.terminated = true;
return this.ansi.reset();
}
/**
* 将当前的渲染内容以 ASCII 字符串形式返回
*/
public String ansiString() {
return this.end().toString();
}
/**
* 输出当前的渲染内容
*/
public void output() {
System.out.println(this.ansiString());
}
}

高度可定制化测试

  测试样例:

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
@Test
public void test02() throws Exception {
ColorfulRender.custom()
.bright() // 开启亮色调渲染
.fg() // 开启前景色渲染
.fgGreen() // 切换前景色为绿色
.render("Green") // 追加绿色的 Green 字样
.fgRed() // 切换前景色为红色
.andThen("Red") // 追加红色的 Red 字样
.fgOff() // 关闭前景色渲染,此时所有对前景色的渲染请求都失效
.bg() // 开启背景色渲染
.bgYellow().fgGreen() // 切换为黄色背景,由于关闭了前景色渲染,因此前景色是默认的白色而不会切换成绿色
.render("BgColorYellow") // 追加黄底白字的 BgColorYellow 字样
.brightOff().fg() // 关闭亮色调渲染(切换成暗色调渲染),打开前景色渲染
.fgRed() // 前景色切换成红色
.bgOff() // 关闭背景色渲染
.render("BgYellowFgRed") // 追加红字的 BgYellowFgRed 字样
.newLine() // 换行
.bold() // 开启黑体渲染
.fgGreen()
.andThen("BoldGreen") // 追加黑体绿色的 BoldGreen 字样
.negative() // 开启反显渲染
.render("BgGreenFgBlack") // 追加黑体、绿底黑字的 BgGreenFgBlack 字样
.boldOff().negativeOff() // 关闭黑体渲染,反显渲染
.underline() // 开启下划线渲染
.render("Underline") // 追加绿字的 Underline 字样
.bright()
.fgOff().bgOff().underlineOff()
.render("Normal") // 追加默认颜色的 Normal 字样
// end、ansiString、output 这三个方法调用过之后当前的渲染就终止了,再次执行渲染操作或者终止操作都将抛出 IllegalStateException
// .end() // 得到 Ansi 对象
// .ansiString() // 得到经过 AnsiColor 渲染的字符串
.output(); // 直接在控制台打印
}

  测试结果:

高度可定制化控制台输出测试.jpg

  IDEA目前仅支持黑体、反显、下划线这三种特效。Eclipse在关闭前、背景色渲染之后无法恢复成默认渲染!

  生产环境中一般都是将日志输出到文件中,所以基本不会使用jansi来实现控制台输出颜色渲染。有人可能会问,那你写这么多有什么用?我只能说这是个人的爱好吧!另外日志框架logback也是支持定制日志输出级别的颜色(就是有些丑,都是暗色调渲染),有需要请自行搜索!

  本文源码已经托管在GitHub上的个人工具包项目中的一小部分,可以自行下载参考:GitHub1 或者 GitHub2

编写日期:2017-06-03
发布日期:2017-06-03

下篇预告

  使用Protostuff提高(反)序列化效率