最简单方案是用std::cout输出ansi转义序列,但需封装避免格式错误、windows需启用虚拟终端、正则匹配要适配语法结构、重定向时须检测tty并支持no_color环境变量。

用 std::cout 直接输出 ANSI 颜色转义序列最简单
不需要第三方库,C++ 标准流本身就能发 ANSI 转义码。关键不是“怎么高亮”,而是“怎么让颜色码不污染逻辑、不写错格式”。[(或 [)是起点,后面跟样式码+m,比如 [32m 是绿色文字,[0m 重置。
- Windows 控制台默认不启用 ANSI 支持(Win10 1511+ 可开,但新项目别赌这个),
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_VIRTUAL_TERMINAL_PROCESSING)必须调一次,否则全是乱码 - 别手写
"[33m" + key + "[0m"这种拼接——容易漏重置、难维护。封装成函数更稳,比如colorize(const std::string&, Color) - 颜色码里别混用空格或换行:
"[1;31m"合法,"[1 ; 31 m"会失效
std::regex 匹配配置项时,注意贪婪匹配和换行边界
配置文件常见格式如 key = value # comment,想把 key 标绿、= 标黄、value 标蓝、注释标灰,得靠正则分段提取再染色。但 std::regex 默认不支持 DOTALL,. 不匹配换行符,单行匹配够用,跨行注释(如 # multi
line)就崩。
- 用
std::regex_constants::ECMAScript | std::regex_constants::icase足够,别滥用basic或awk语法,C++17 的std::regex实现有差异,POSIX 扩展在 MSVC 上基本不可用 - 匹配
key = value时,写成R"(^s*(w+)s*=s*(.*?)(?:s+#.*)?$)"比.*更安全,避免吃掉下一行 - 实际替换不能直接用
std::regex_replace塞颜色码——它会把整个匹配当整体替换,无法对捕获组单独着色。得用std::sregex_iterator遍历,逐段处理
不同配置语法的 token 边界必须显式定义,不能依赖空格切分
INI 风格的 [section]、JSON 风格的 "key": "val"、Shell 风格的 KEY="val",token 划分规则完全不同。拿同一个正则硬套,key 和引号、等号、括号全会粘连错位。
- INI:节头用
R"([([^]]+)])"单独抓,键值对用=左右空白做分隔,值部分要跳过引号包裹内容("a=b"里的=不算分隔符) - JSON:必须先跳过字符串字面量(
"..."..."),否则:和逗号会被误判;std::regex不支持递归匹配,简单场景可用状态机预扫,复杂情况建议用nlohmann/json解析后再染色 - Shell:等号前后无空格是合法的(
PATH=/bin:/usr/bin),但变量展开$HOME和${HOME}要分开识别,否则$会被当成普通字符
ANSI 颜色在重定向或管道中会变成乱码,必须检测输出目标
用户执行 ./cfgcat config.ini | less 时,std::cout.isatty() 返回 false,此时输出颜色码只会污染管道数据。不判断就硬发,下游工具(如 grep、日志系统)会崩溃或显示异常字符。
立即学习“C++免费学习笔记(深入)”;
- Linux/macOS 下用
isatty(STDOUT_FILENO)最可靠;Windows 下用_isatty(_fileno(stdout)),别信std::cout.rdbuf()->in_avail()这类伪判定 - 环境变量
NO_COLOR=1是事实标准,检查std::getenv("NO_COLOR")并优先于 tty 判断 - 别用
system("tput setaf 2")——它启动子进程、慢、且 tput 不一定在 PATH 里,纯 C++ 方案更可控
真正麻烦的是嵌套结构的嵌套颜色,比如 JSON 里字符串中的转义序列 "\n" 该不该高亮反斜杠?这种细节没人替你决定,得按实际配置语言规范来切分 token,而不是靠“看起来像关键字”就上色。










