如何合理的在 Windows 命令提示符中输出带颜色文本

· · 科技·工程

引子

前几天使用 Linux 系统时,发现一些命令行工具的输出具有颜色。

带颜色的输出有一个优点,可以轻松的分辨信息的关键程度。

在 Windows 系统下,其实也可以在命令提示符中输出彩色文本。

实现方法

转义字符法

与 Linux 一样,Windows 系统下的 Windows Terminal 终端也可以识别转义字符

颜色表如下所示:

转义字符 颜色
\033[01;30m 黑色
\033[01;31m 红色
\033[01;32m 绿色
\033[01;33m 黄色
\033[01;34m 蓝色
\033[01;35m 紫色
\033[01;36m 青色
\033[01;37m 白色
\033[01;40m 背景黑色
\033[01;41m 背景红色
\033[01;42m 背景绿色
\033[01;43m 背景黄色
\033[01;44m 背景蓝色
\033[01;45m 背景紫色
\033[01;46m 背景青色
\033[01;47m 背景白色
\033[00m 清除颜色

这样做实际有一个弊端,对于传统的 cmd.exe 或 powershell.exe 来说,因为太过老旧,它们不识别这些转义字符。

所以这不算是一个好的方法,对于 Windows 系统,还得使用 Windows API。

Windows API 调用法

对于控制台的输出彩色文本操作,Microsoft 提供了一些 API。

主要是使用 SetConsoleTextAttribute() 函数控制输出文本的颜色

标程如下:

#include <iostream>
#include <Windows.h>

int main(){
  ::HANDLE handleOfConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); // 获取控制台句柄,不理解的直接死记硬背
  ::SetConsoleTextAttribute(handleOfConsole, FOREGROUND_RED); // 设置输出为红色
  std::cout << "Hello World" << std::endl; // 输出一个红色的 Hello World
  ::SetConsoleTextAttribute(handleOfConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); // 恢复原状
  return 0;
}

其中的 FOREGROUND_XXX 是 C++ 宏 如下表所示

C++ 宏名称 颜色
FOREGROUND_RED 红色
FOREGROUND_GREEN 绿色
FOREGROUND_BLUE 蓝色
FOREGROUND_INTENSITY 高亮显示
BACKGROUND_RED 背景红色
BACKGROUND_GREEN 背景绿色
BACKGROUND_BLUE 背景蓝色
BACKGROUND_INTENSITY 背景高亮显示

就像美术课上教的那样,这些颜色可以混合。用 | 号混合

例如:

这样的方法可以稳定的在 Windows 系统的命令提示符中输出颜色文本,无论版本。

但是这样的代码不易读,所以可以封装一下

示例如下,我只封装了前景色中的红、绿、蓝和恢复原状,有兴趣的可以自行封装:

// TerminalColor.h
#include <iostream>
#include <Windows.h>

class TerminalColor {
    public:
        static std::ostream& Reset(std::ostream& os);
        static std::ostream& Red(std::ostream& os);
        static std::ostream& Green(std::ostream& os);
        static std::ostream& Blue(std::ostream& os);
    };

// TerminalColor.cpp
#include "TerminalColor.h"
::HANDLE handleOfConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);

std::ostream& TerminalColor::Reset(std::ostream& os) { 
    ::SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0004 | 0x0002 | 0x0001);
    return os; 
}

std::ostream& TerminalColor::Red(std::ostream& os) { 
    ::SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0004);
    return os; 
}

std::ostream& TerminalColor::Green(std::ostream& os) { 
    ::SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0002);
    return os; 
}

std::ostream& TerminalColor::Blue(std::ostream& os) { 
    ::SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0001);
    return os; 
}

其中的十六进制数字是 C++ 宏展开后的结果

尾声