Windows中使用C语言实现打印彩色文字到命令行窗口

目录

Windows代码的实现

扩展:Windows、Linux跨平台代码的编写


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

C语言VA_LIST详解-CSDN博客

3、vsprintf函数,这部分知识请参考:

C 库函数 – 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;
}

参考文章

        文中所有链接指向的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值