目录
Windows代码的实现
本文记录的是在Windows中使用C语言自定义一个可以打印彩色文字的printf函数,函数原型如下:
int colorful_printf(WORD color, const char* format, ...);
形参color接收设定的颜色,形参format是printf函数的格式字符串,后面的不确定个数的参数是printf函数的格式输出符对应的待打印项。也就是将传给printf函数的实参原封不动的传过来,再按照参数color指定的颜色进行打印。
要实现这个功能,需要预先掌握如下几方面的知识:
1、在Windows中,实现彩色打印用的是Windows的API函数SetConsoleTextAttribute,这部分知识请参考:
https://baike.baidu.com/item/SetConsoleTextAttribute/570121?fr=aladdin
https://baike.baidu.com/item/GetStdHandle/6909878?fr=aladdin
C/C++ 中怎样使用SetConsoleTextAttribute()函数来控制输出字符的颜色_C 语言_脚本之家
2、不确定个数的参数的处理,需要使用va_list,这部分知识请参考:
https://baike.baidu.com/item/va_list/8573665?fr=aladdin
3、vsprintf函数,这部分知识请参考:
掌握了以上的知识后,就可以动手实现我们的函数了。比较简单,直接上代码
#pragma warning(disable:4996)
#include <Windows.h>
#include <stdio.h>
#include <stdarg.h>
#define BLUE (FOREGROUND_INTENSITY | FOREGROUND_BLUE)
#define GREEN (FOREGROUND_INTENSITY | FOREGROUND_GREEN)
#define RED (FOREGROUND_INTENSITY | FOREGROUND_RED)
#define CYAN (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN)
#define PURPLE (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED)
#define YELLOW (FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED)
#define WHITEBGBLACKFONT ((BACKGROUND_INTENSITY | (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED)) | (FOREGROUND_INTENSITY | !(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)))
#define DEFAULT (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
int main(void)
{
int colorful_printf(WORD color, const char* format, ...);
colorful_printf(RED, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(GREEN, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(BLUE, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(CYAN, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(PURPLE, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(YELLOW, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(WHITEBGBLACKFONT, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
return 0;
}
/// <summary>
/// 彩色打印
/// </summary>
/// <param name="color">预期的颜色</param>
/// <param name="format">要打印的格式化字符串</param>
/// <param name="">格式化输出的变量</param>
/// <returns>打印的字符个数(单个汉字或中文标点算两个字符)</returns>
int colorful_printf(WORD color, const char* format, ...)
{
char str[256];// 要输出的字符串
va_list ap;// 指向参数的指针
va_start(ap, format);// 初始化指向参数的指针
HANDLE consolehwnd = GetStdHandle(STD_OUTPUT_HANDLE);// 输出窗口的句柄
SetConsoleTextAttribute(consolehwnd, color);// 改为使用预期的颜色输出
// 按预期的颜色输出
int ret = vsprintf(str, format, ap);
printf("%s", str);
SetConsoleTextAttribute(consolehwnd, DEFAULT);// 恢复默认效果
va_end(ap);// 结束可变参数的获取
return ret;
}
打印的效果:
这里记录几个技术要点:
1、要输出的字符串放在字符数组str中,一定要确保数组的长度足以容纳要打印的字符串,否则打印的时候会崩溃。
2、在这里我自定义了一些宏,全部使用了高亮效果,颜色组合的结果是百度查的,如果不够准确还请见谅。宏定义写的比较复杂,实际上完全可以用实际的数值来定义。比如宏DEFAULT,是命令行窗口的默认输出效果,完全可以写成:
#define DEFAULT 0x0007
3、colorful_printf函数的返回值实际上就是vsprintf函数的返回值,表示打印的字符的个数。一个汉字或中文标点算两个字符。
4、在单线程程序中,不会有什么问题。在多线程程序中,如果线程A使用colorful_printf函数进行输出,在内部调用SetConsoleTextAttribute()设置颜色之后,如果线程B恰好在此时进行打印,就会按照线程A中设置的颜色来打印了,这可能不是我们想要的效果。用这个程序无法完全规避这个问题,因为不同的线程都是往一个窗口(就是SetConsoleTextAttribute()的参数hConsoleOutput标识的窗口)中打印。因此只能尽可能地缩短带颜色打印的时间,就是说,调用SetConsoleTextAttribute()设置颜色之后,马上调用vsprintf()和printf()打印,然后再马上调用SetConsoleTextAttribute()恢复为默认效果。如果哪位大神知道适用于多线程,严格区分不同线程的打印颜色的方案,也欢迎指教。
扩展:Windows、Linux跨平台代码的编写
Linux中此功能的实现,别人已经写的很好了,我直接转过来:Linux下改变printf输出颜色和格式
我们可以通过条件编译,分别编写Windows、Linux的实现代码,而保持调用语句不变。经实测Linux的亮、暗色(粗体和非粗体)看不出太大区别,索性不区分了。Linux中我们是将背景色和字体颜色拼成的字符串作为参数传递。
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdarg.h>
#ifdef _WIN32
#include <Windows.h>
//#include "vld.h"
#define BLUE (FOREGROUND_INTENSITY | FOREGROUND_BLUE)
#define GREEN (FOREGROUND_INTENSITY | FOREGROUND_GREEN)
#define RED (FOREGROUND_INTENSITY | FOREGROUND_RED)
#define CYAN (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN)
#define PURPLE (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED)
#define YELLOW (FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED)
#define WHITEBGBLACKFONT ((BACKGROUND_INTENSITY | (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED)) | (FOREGROUND_INTENSITY | !(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)))
#define DEFAULT (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
/// <summary>
/// 彩色打印
/// </summary>
/// <param name="color">预期的颜色</param>
/// <param name="format">要打印的格式化字符串</param>
/// <param name="">格式化输出的变量</param>
/// <returns>打印的字符个数(单个汉字或中文标点算两个字符)</returns>
int colorful_printf(WORD color, const char* format, ...)
{
char str[256];// 要输出的字符串
va_list ap;// 指向参数的指针
va_start(ap, format);// 初始化指向参数的指针
HANDLE consolehwnd = GetStdHandle(STD_OUTPUT_HANDLE);// 输出窗口的句柄
SetConsoleTextAttribute(consolehwnd, color);// 改为使用预期的颜色输出
// 按预期的颜色输出
int ret = vsprintf(str, format, ap);
printf("%s", str);
SetConsoleTextAttribute(consolehwnd, DEFAULT);// 恢复默认效果
va_end(ap);// 结束可变参数的获取
return ret;
}
#else
#include <string.h>
#include <stdlib.h>
// Linux的彩色打印不区分粗体和非粗体
#define BLUE "0;34"
#define DARKBLUE BLUE
#define GREEN "0;32"
#define DARKGREEN GREEN
#define RED "0;31"
#define DARKRED RED
#define CYAN "0;36"
#define DARKCYAN CYAN
#define PURPLE "0;35"
#define DARKPURPLE PURPLE
#define YELLOW "0;33"
#define DARKYELLOW YELLOW
#define DEFAULT "0;0"
#define WHITEBGBLACKFONT "47;30"
/// <summary>
/// 彩色打印
/// </summary>
/// <param name="color">预期的颜色</param>
/// <param name="format">要打印的格式化字符串</param>
/// <param name="">格式化输出的变量</param>
/// <returns>打印的字符个数(单个汉字或中文标点算两个字符)</returns>
int colorful_printf(const char* color, const char* format, ...)
{
char str[200000];// 待打印的原始字符串
char color_str[250000] = "\033[22;";// 最终打印的彩色字符串,显示方式固定为非粗体
va_list ap;// 指向参数的指针
va_start(ap, format);// 初始化指向参数的指针
int ret = vsprintf(str, format, ap);// 将待打印的原始字符串存入str中
strcat(color_str, color);// 拼接背景和字体颜色
strcat(color_str, "m");// 拼接"m"
strcat(color_str, str);// 拼接待打印的原始字符串
strcat(color_str, "\033[0m");// 拼接"\033[0m"
printf("%s", color_str);// 按预期的颜色输出
va_end(ap);// 结束可变参数的获取
return ret;
}
#endif
int main(void)
{
colorful_printf(RED, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(GREEN, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(BLUE, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(CYAN, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(PURPLE, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(YELLOW, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
colorful_printf(WHITEBGBLACKFONT, "%d年北京冬奥会的口号是:%s\n", 2022, "一起向未来!");
return 0;
}
参考文章
文中所有链接指向的文章