我该如何在Java中着色输出?
比如说,在C和其他编程语言中,我可以使用类似于ANSI转义序列的\033[0m
来实现这一点。但是在Java中却不行。
public static void main(String[] x) {
System.out.println("\033[0m BLABLA \033[0m\n");
}
我该如何在Java中着色输出?
比如说,在C和其他编程语言中,我可以使用类似于ANSI转义序列的\033[0m
来实现这一点。但是在Java中却不行。
public static void main(String[] x) {
System.out.println("\033[0m BLABLA \033[0m\n");
}
你可能无法为Windows的cmd提示符
设置颜色,但它应该适用于许多Unix(或类Unix)终端。
另外,请注意,一些终端可能根本不支持一些(如果有的话)ANSI转义序列,特别是24位颜色。
请参考底部的Curses部分以获取最佳解决方案。对于个人或简单的解决方案(虽然不是跨平台解决方案),请参考ANSI转义序列部分。
java: System.out.println((char)27 + "[31m" + "ERROR MESSAGE IN RED");
python: print(chr(27) + "[31m" + "ERROR MESSAGE IN RED")
printf '\x1b[31mERROR MESSAGE IN RED'
printf '\e[31mERROR MESSAGE IN RED'
printf '
CTRL+V,CTRL+[[31mERROR MESSAGE IN RED'
^[
。虽然它看起来像两个字符,但实际上只是一个ESC字符。虽然这不是最好的做法,但在编程或脚本语言中最简单的方法是使用转义序列。来自该链接的内容如下:
转义序列是一系列用于改变计算机及其附加外围设备状态的字符。它们也被称为控制序列,反映了它们在设备控制方面的用途。
然而,在视频文本终端中,使用ANSI转义序列会更容易。来自该链接的内容如下:
ANSI转义序列是一种标准的信令方式,用于控制视频文本终端上的光标位置、颜色和其他选项。某些字节序列,大多以“Esc”和“[''开始,嵌入到文本中,终端会将其解析为命令,而不是字符代码。
27
/ 十六进制:0x1B
)开始。一些编程语言(如Java)不会将\e
或\x1b
解释为ESC字符。但是,我们知道ASCII字符27
是 ESC 字符,因此我们可以将27
强制转换为char
并使用它来开始转义序列。
以下是在常见编程语言中执行此操作的一些方法:
Java
System.out.println((char)27 + "[33m黄色");
Python 3
print(chr(27) + "[34m蓝色");
print("\x1b[35m洋红色");
\x1b
在Python中也可以正确解释Node JS
console.log(String.fromCharCode(27) + "[36m青色");
console.log("\x1b[30;47m黑色背景白色字体");
\x1b
在node中也可以使用printf '\e[31mRED'
printf '\x1b[31mRED'
如果两者都有颜色,那就太好了!如果只有一个有颜色,则使用该序列。如果两者都没有颜色,请仔细检查你输入的内容是否正确,并确保你正在使用bash或zsh;如果你仍然看不到任何颜色,则你的终端可能不支持ANSI转义序列。
如果我没记错的话,Linux 终端通常支持\e
和 \x1b
转义序列,而 OS X 终端通常只支持 \e
,但我可能错了。不过,如果你看到类似下面的图片,那么你就准备好了!(请注意,我正在使用 shell zsh,它正在着色我的提示字符串;另外,我在 Linux 中使用 urxvt 作为终端。)
你可能会问:"这是如何工作的?"。基本上,printf
解释了接下来的字符序列(即 单引号内的所有内容)。当 printf
遇到 \e
或 \x1b
时,它将把这些字符转换为 ESC 字符(ASCII:27)。这正是我们想要的。现在,printf
发送了 ESC31m
,由于有一个 ESC 后跟一个有效的 ANSI 转义序列,因此我们应该得到彩色输出(只要终端支持)。echo -e '\e[32mGREEN'
(例如)来着色输出。请注意,echo
的 -e
标志 "启用反斜杠转义的解释",如果您希望 echo
正确解释转义序列,则必须使用该标志。
ANSI 转义序列不仅可以用于输出颜色,但我们先从这个开始,详细了解颜色是如何工作的。然后,我们将看到如何操纵光标。最后,我们将介绍如何使用 8 位颜色和 24 位颜色(虽然它只有薄弱的支持)。
在 Wikipedia 上,他们将 ESC[ 称为 CSI
,所以我也将这样做。
使用 ANSI 转义序列实现彩色输出,请使用以下代码:
CSI
n
m
CSI
: 转义字符—^[[
或 ESC[n
: 一个数字,下列之一:
30
-37
, 39
: 前景色40
-47
, 49
: 背景色m
: 一个字面上的 ASCII m
—终止转义序列我将使用 bash 或 zsh 来演示所有可能的颜色组合。将以下内容放入 bash 或 zsh 中以查看结果 (您可能需要将 \e
替换为 \x1b
):
for fg in {30..37} 39; do for bg in {40..47} 49; do printf "\e[${fg};${bg}m~TEST~"; done; printf "\n"; done;
结果:
+~~~~~~+~~~~~~+~~~~~~~~~~~+
| fg | bg | color |
+~~~~~~+~~~~~~+~~~~~~~~~~~+
| 30 | 40 | black |
| 31 | 41 | red |
| 32 | 42 | green |
| 33 | 43 | yellow |
| 34 | 44 | blue |
| 35 | 45 | magenta |
| 36 | 46 | cyan |
| 37 | 47 | white |
| 39 | 49 | default |
+~~~~~~+~~~~~~+~~~~~~~~~~~+
SGR只允许您更改文本。其中许多在某些终端中不起作用,因此在生产级项目中要谨慎使用。但是,它们可以有助于使程序输出更易读或帮助您区分不同类型的输出。
颜色实际上属于SGR,因此语法相同:
CSI
n
m
CSI
: 转义字符—^[[
或 ESC[n
: 一个数字,可以是以下数字之一:
0
: 重置1
-9
: 打开各种文本效果21
-29
: 关闭各种文字效果(比 1
-9
更少支持)30
-37
, 39
: 前景色40
-47
, 49
: 背景色38
: 8位或24位前景色(参见下面的8/24位颜色)48
: 8位或24位背景色(参见下面的8/24位颜色)m
: 一个字面上的 ASCII m
—终止转义序列printf '\e[34;47;1;3mCRAZY TEXT\n'
将显示 CRAZY TEXT
,具有 蓝色前景
和 白色背景
,并且它将是 粗体
和 斜体
的。例如:
将以下内容放入您的bash或zsh shell中,以查看您可以执行的所有文本效果。(您可能需要将\e
替换为\x1b
。)
for i in {1..9}; do printf "\e[${i}m~TEST~\e[0m "; done
结果:
你可以看到,我的终端支持除了暗淡(2)、隐藏(8)和删除线(9)之外的所有文本效果。+~~~~~+~~~~~~~~~~~~~~~~~~+
| n | effect |
+~~~~~+~~~~~~~~~~~~~~~~~~+
| 0 | reset |
| 1 | bold |
| 2 | faint* |
| 3 | italic** |
| 4 | underline |
| 5 | slow blink |
| 6 | rapid blink* |
| 7 | inverse |
| 8 | conceal* |
| 9 | strikethrough* |
+~~~~~+~~~~~~~~~~~~~~~~~~+
* not widely supported
** not widely supported and sometimes treated as inverse
虽然大多数终端都支持此功能,但它比0-7
、9
颜色的支持要少。
语法:
CSI
38;5;
n
m
CSI
:转义字符—^[[
或ESC[38;5;
:表示使用8位色彩的文字字符串,用于前景色n
:数字之一,如下所示:
0
-255
如果您想以一种漂亮的方式预览终端中的所有颜色,我在gist.github.com上有一个不错的脚本。
它看起来像这样:
如果您想使用8位颜色更改背景,只需将38
替换为48
:
CSI
48;5;
n
m
CSI
:转义字符—^[[
或ESC[48;5;
:表示使用8位颜色作为背景的文字字符串n
:数字之一:
0
-255
也称为真彩色,24位彩色提供了一些非常酷的功能。支持此功能的终端正在不断增加(据我所知,在除了我的终端urxvt之外的大多数现代终端中都可以使用)。
24位色实际上在vim中得到支持(请参见vim wiki以了解如何启用24位色)。这很棒,因为它从gvim定义的配色方案中提取;例如,它使用highlight guibg=#______ guifg=#______
中的fg/bg来生成24位颜色!很不错,对吧?CSI
38;2;
r
;
g
;
b
m
- CSI
:转义字符——^[[
或ESC[
- 38;2;
:表示前景色使用24位颜色的文字字符串
- r
、g
、b
:数字——每个数字应该是0-255之间的数
为了测试你可以使用的多种颜色(我想是(2^8)^3
或者 2^24
或者 16777216
种可能性),你可以在 bash 或 zsh 中使用以下命令:
for r in 0 127 255; do for g in 0 127 255; do for b in 0 127 255; do printf "\e[38;2;${r};${g};${b}m($r,$g,$b)\e[0m "; done; printf "\n"; done; done;
结果(这是在 gnome-terminal 中,因为urxvt 不支持 24 位色彩……请好好工作,urxvt 维护人员……):
如果您想要24位颜色作为背景...您猜对了! 您只需将38
替换为48
:
CSI
48;2;
r
;
g
;
b
m
CSI
: 转义字符—^[[
或 ESC[48;2;
: 字面字符串,表示使用24位颜色作为背景r
,g
,b
: 数字—每个数字应该是0
-255
有时候\e
和\x1b
无法工作。例如,在sh shell中,有时候两者都不起作用(尽管它在我的系统上现在可以,我认为以前不行)。
^[
,但不要担心;它只是一个字符,而不是两个。请参考Curses (Programming Library) page,以获取有关curses的完整参考信息。需要注意的是,curses仅适用于类unix操作系统。
我不会详细介绍,因为搜索引擎可以显示链接到可以比我更好地解释这一点的网站,但我将在此简要讨论并给出一个示例。
如果您阅读上面的文本,您可能会记得\e
或\x1b
有时会与printf
一起使用。嗯,有时\e
和\x1b
根本不起作用(这不是标准,我从未使用过这样的终端,但这是可能的)。更重要的是,更复杂的转义序列(想象一下Home和其他多字符键)很难支持每个终端(除非您愿意花费大量时间和精力解析terminfo和termcap并找出如何处理每个终端)。
import curses
def main(stdscr):
# allow curses to use default foreground/background (39/49)
curses.use_default_colors()
# Clear screen
stdscr.clear()
curses.init_pair(1, curses.COLOR_RED, -1)
curses.init_pair(2, curses.COLOR_GREEN, -1)
stdscr.addstr("ERROR: I like tacos, but I don't have any.\n", curses.color_pair(1))
stdscr.addstr("SUCCESS: I found some tacos.\n", curses.color_pair(2))
stdscr.refresh() # make sure screen is refreshed
stdscr.getkey() # wait for user to press key
if __name__ == '__main__':
curses.wrapper(main)
结果:
你可能会认为这是一种更加迂回的做法,但实际上它更加跨平台(在Unix和类Unix平台世界中真正跨终端)。对于颜色而言,这并不是非常重要,但是当涉及到支持其他多序列转义序列(例如Home、End、Page Up、Page Down等)时,curses变得更加重要。
tput
是一个用于操作光标和文本的命令行实用程序tput
随同curses
软件包一起提供。如果您希望在终端中使用跨终端(ish)应用程序,则应使用tput,因为它解析terminfo或其他所需内容,并使用一组标准化命令(如curses),然后返回正确的转义序列。echo "$(tput setaf 1)$(tput bold)ERROR:$(tput sgr0)$(tput setaf 1) My tacos have gone missing"
echo "$(tput setaf 2)$(tput bold)SUCCESS:$(tput sgr0)$(tput setaf 2) Oh good\! I found my tacos\!"
结果:
这对我很有效:
System.out.println((char)27 + "[31mThis text would show up red" + (char)27 + "[0m");
您需要使用结尾标记"[37m]"才能将颜色恢复为白色(或其他颜色)。如果您不这样做,可能会使接下来的所有内容都变成“红色”。是的,这完全有可能。
设置类路径=%classpath%; d:\ jansi-1.4.jar;
尝试以下代码:
import org.fusesource.jansi.AnsiConsole;
import static org.fusesource.jansi.Ansi.*;
import static org.fusesource.jansi.Ansi.Color.*;
public class Sample
{
public static void main(String[] args)
{
AnsiConsole.systemInstall();
System.out.println(ansi().fg(RED).a("Hello World").reset());
System.out.println("My Name is Raman");
AnsiConsole.systemUninstall();
}
}
以下是Win32控制台的解决方案。
1)在这里获取JavaNativeAccess库:https://github.com/twall/jna/
2)这两个Java类可以解决问题。
祝使用愉快。
package com.stackoverflow.util;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Structure;
public class Win32 {
public static final int STD_INPUT_HANDLE = -10;
public static final int STD_OUTPUT_HANDLE = -11;
public static final int STD_ERROR_HANDLE = -12;
public static final short CONSOLE_FOREGROUND_COLOR_BLACK = 0x00;
public static final short CONSOLE_FOREGROUND_COLOR_BLUE = 0x01;
public static final short CONSOLE_FOREGROUND_COLOR_GREEN = 0x02;
public static final short CONSOLE_FOREGROUND_COLOR_AQUA = 0x03;
public static final short CONSOLE_FOREGROUND_COLOR_RED = 0x04;
public static final short CONSOLE_FOREGROUND_COLOR_PURPLE = 0x05;
public static final short CONSOLE_FOREGROUND_COLOR_YELLOW = 0x06;
public static final short CONSOLE_FOREGROUND_COLOR_WHITE = 0x07;
public static final short CONSOLE_FOREGROUND_COLOR_GRAY = 0x08;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_BLUE = 0x09;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_GREEN = 0x0A;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_AQUA = 0x0B;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_RED = 0x0C;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_PURPLE = 0x0D;
public static final short CONSOLE_FOREGROUND_COLOR_LIGHT_YELLOW = 0x0E;
public static final short CONSOLE_FOREGROUND_COLOR_BRIGHT_WHITE = 0x0F;
public static final short CONSOLE_BACKGROUND_COLOR_BLACK = 0x00;
public static final short CONSOLE_BACKGROUND_COLOR_BLUE = 0x10;
public static final short CONSOLE_BACKGROUND_COLOR_GREEN = 0x20;
public static final short CONSOLE_BACKGROUND_COLOR_AQUA = 0x30;
public static final short CONSOLE_BACKGROUND_COLOR_RED = 0x40;
public static final short CONSOLE_BACKGROUND_COLOR_PURPLE = 0x50;
public static final short CONSOLE_BACKGROUND_COLOR_YELLOW = 0x60;
public static final short CONSOLE_BACKGROUND_COLOR_WHITE = 0x70;
public static final short CONSOLE_BACKGROUND_COLOR_GRAY = 0x80;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_BLUE = 0x90;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_GREEN = 0xA0;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_AQUA = 0xB0;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_RED = 0xC0;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_PURPLE = 0xD0;
public static final short CONSOLE_BACKGROUND_COLOR_LIGHT_YELLOW = 0xE0;
public static final short CONSOLE_BACKGROUND_COLOR_BRIGHT_WHITE = 0xF0;
// typedef struct _COORD {
// SHORT X;
// SHORT Y;
// } COORD, *PCOORD;
public static class COORD extends Structure {
public short X;
public short Y;
}
// typedef struct _SMALL_RECT {
// SHORT Left;
// SHORT Top;
// SHORT Right;
// SHORT Bottom;
// } SMALL_RECT;
public static class SMALL_RECT extends Structure {
public short Left;
public short Top;
public short Right;
public short Bottom;
}
// typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
// COORD dwSize;
// COORD dwCursorPosition;
// WORD wAttributes;
// SMALL_RECT srWindow;
// COORD dwMaximumWindowSize;
// } CONSOLE_SCREEN_BUFFER_INFO;
public static class CONSOLE_SCREEN_BUFFER_INFO extends Structure {
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
// Source: https://github.com/twall/jna/nonav/javadoc/index.html
public interface Kernel32 extends Library {
Kernel32 DLL = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
// HANDLE WINAPI GetStdHandle(
// __in DWORD nStdHandle
// );
public int GetStdHandle(
int nStdHandle);
// BOOL WINAPI SetConsoleTextAttribute(
// __in HANDLE hConsoleOutput,
// __in WORD wAttributes
// );
public boolean SetConsoleTextAttribute(
int in_hConsoleOutput,
short in_wAttributes);
// BOOL WINAPI GetConsoleScreenBufferInfo(
// __in HANDLE hConsoleOutput,
// __out PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
// );
public boolean GetConsoleScreenBufferInfo(
int in_hConsoleOutput,
CONSOLE_SCREEN_BUFFER_INFO out_lpConsoleScreenBufferInfo);
// DWORD WINAPI GetLastError(void);
public int GetLastError();
}
}
package com.stackoverflow.util;
import java.io.PrintStream;
import com.stackoverflow.util.Win32.Kernel32;
public class ConsoleUtil {
public static void main(String[] args)
throws Exception {
System.out.print("abc");
static_color_print(
System.out,
"def",
Win32.CONSOLE_BACKGROUND_COLOR_RED,
Win32.CONSOLE_FOREGROUND_COLOR_BRIGHT_WHITE);
System.out.print("def");
System.out.println();
}
private static Win32.CONSOLE_SCREEN_BUFFER_INFO _static_console_screen_buffer_info = null;
public static void static_save_settings() {
if (null == _static_console_screen_buffer_info) {
_static_console_screen_buffer_info = new Win32.CONSOLE_SCREEN_BUFFER_INFO();
}
int stdout_handle = Kernel32.DLL.GetStdHandle(Win32.STD_OUTPUT_HANDLE);
Kernel32.DLL.GetConsoleScreenBufferInfo(stdout_handle, _static_console_screen_buffer_info);
}
public static void static_restore_color()
throws Exception {
if (null == _static_console_screen_buffer_info) {
throw new Exception("Internal error: Must save settings before restore");
}
int stdout_handle = Kernel32.DLL.GetStdHandle(Win32.STD_OUTPUT_HANDLE);
Kernel32.DLL.SetConsoleTextAttribute(
stdout_handle,
_static_console_screen_buffer_info.wAttributes);
}
public static void static_set_color(Short background_color, Short foreground_color) {
int stdout_handle = Kernel32.DLL.GetStdHandle(Win32.STD_OUTPUT_HANDLE);
if (null == background_color || null == foreground_color) {
Win32.CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info =
new Win32.CONSOLE_SCREEN_BUFFER_INFO();
Kernel32.DLL.GetConsoleScreenBufferInfo(stdout_handle, console_screen_buffer_info);
short current_bg_and_fg_color = console_screen_buffer_info.wAttributes;
if (null == background_color) {
short current_bg_color = (short) (current_bg_and_fg_color / 0x10);
background_color = new Short(current_bg_color);
}
if (null == foreground_color) {
short current_fg_color = (short) (current_bg_and_fg_color % 0x10);
foreground_color = new Short(current_fg_color);
}
}
short bg_and_fg_color =
(short) (background_color.shortValue() | foreground_color.shortValue());
Kernel32.DLL.SetConsoleTextAttribute(stdout_handle, bg_and_fg_color);
}
public static<T> void static_color_print(
PrintStream ostream,
T value,
Short background_color,
Short foreground_color)
throws Exception {
static_save_settings();
try {
static_set_color(background_color, foreground_color);
ostream.print(value);
}
finally {
static_restore_color();
}
}
public static<T> void static_color_println(
PrintStream ostream,
T value,
Short background_color,
Short foreground_color)
throws Exception {
static_save_settings();
try {
static_set_color(background_color, foreground_color);
ostream.println(value);
}
finally {
static_restore_color();
}
}
}
最简单的方法是在Cygwin控制台中运行您的程序(未修改)。
第二简单的方法是在普通Windows控制台中运行您的程序(也未修改),通过tee.exe(来自Cygwin或Git分发)将输出传输。Tee.exe将识别转义码并调用适当的WinAPI函数。
类似于:
java MyClass | tee.exe log.txt
java MyClass | tee.exe /dev/null
jar
库。转义序列必须由某个程序进行解释,才能将其转换为颜色。当使用命令行启动Java时,标准的CMD.EXE不支持此功能,因此Java也不支持。
(char) 27
也可以用字符(char)
表示(它也可以表示为字符串,因为字符串是一系列字符和转义符“\”及其后面的内容直到“m”被视为单个字符)\u001b
或(正如我在ubuntuforums.org上注意到的那样)字符"\003"
。 - dylnmc