C语言控制台字体颜色控制

本文在windows平台下,采用MinGW编译器。

一、一个示例

上学期,带学生C语言课程时,有学生写出了一段错误程序,如下:

// con_error.c
#include <stdio.h>
int main(void)
{
    print("hello,world\n")

    return 0;
}

看到,这个程序中有明显的错误,printf()函数行语句后缺少分号,这将导致错误,如下图

让学生不解的是,他们写的程序都是黑底白字的枯燥输出,而这里,会看到,控制台的某些关键字体颜色改变了,我们来实现,美中不足,这玩应要依赖平台特性。

二、一个控制台命令color

按win+r,在“运行”对话框中输入cmd,调出控制台,默认的控制台颜色是黑底(近似黑色),淡灰色字体,一般默认情况下:

背景颜色RGB值为(12,12,12);

字体颜色RGB值为(204,204,204)。

可以使用控制台命令color改变背景色和前景色。color命令格式为:

color xy

其中x代表背景色,y代表前景色。windows中的定义如下

我们做个试验,启动控制台,看到黑底白字的默认界面,然后输入:

color 24

界面将呈现绿色背景色,红色字体。这个函数有个返回值,保存在系统变量errorlevel中,再执行:

echo %errorlevel%

将返回0。代表执行正确,如下图。

但是,你不能将背景色和前景色设置为相同颜色,再启动一个控制台,执行:

color 22

控制台背景和前景色不会有任何变化,再执行:

echo %errorlevel%

将返回错误代码1,如下图

三、C程序调用cmd命令

在C程序中可以调用cmd命令,使用函数:system(),该函数定义在头文件stdlib.h中,完整格式为:

int system (const char *);

也就是说它有一个返回值,这个函数从vc6.0的时代就存在,有兴趣的可以查看一下它的源码。其输入的字符串为windows的cmd命令,并有一个返回值,做实验。

实验一:system()函数的使用

程序代码如下:

/* fun_system.c */
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    int ret;

    ret = system("mkdir mydir");
    printf("ret = %d\n",ret);

    return 0;
}

然后编译这个程序:

gcc fun_system.c -o fun_system.exe

这个程序完成两项工作,一个是让C程序执行“mkdir mydir”命令,同时得到它的返回值。

运行这个程序后,将在程序目录下生产一个mydir文件夹,同时打印:

ret = 0

同样,返回值0代表顺利执行,我们再次运行这个程序,得到:

子目录或文件 mydir 已经存在。
ret = 1

得到一个错误报告,同时返回为1,这里的“子目录或文件 mydir 已经存在”由mkdir命令产生。

四、C语言使用color命令改变控制台颜色

现在就可以在C语言程序中使用system()函数改变控制台颜色。

实验二:C语言改变控制台颜色——初级版本

程序如下:

/* c_change_conclr.c */
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    int ret;
    ret = system("color 24");
    if(ret == 0) {
        printf("Changed console color is ok.\n");
    } else {
        printf("Error!!!\n");
    }

    return 0;
}

编译程序,执行后,效果如下图:

五、更精细的控制

上面的使用color命令存在严重问题,控制太过于粗糙,还是无法得到最初的改变某个字颜色的效果。我们需要更为精细的控制。

这需要用到windows的平台sdk。涉及的函数可以是:

BOOL SetConsoleTextAttribute(STD_OUTPUT_HANDLE, WORD wAttributes)

关于这个函数有很多高级用法,有兴趣的请参看:

SetConsoleTextAttribute 函数

在上面给出的文档中,有这样一句话:

SetConsoleTextAttribute已经不建议被使用,但是微软公司依然会支持它,网上有很多这方面的讨论,有兴趣的可以参加,下面的博文:

SetConsoleTextAttribute函数用法

我们采用一种更加稳健的方式。使用SetConsoleMode 函数。在

控制台虚拟终端序列

SetConsoleMode 函数

两篇文章中,你可以找到这个函数的用法,我们用这个函数来解决最初的问题。函数定义如下:

BOOL WINAPI SetConsoleMode(
  _In_ HANDLE hConsoleHandle,
  _In_ DWORD  dwMode
);
hConsoleHandle为控制台输入缓冲区或控制台屏幕缓冲区的句柄;
dwMode要设置的输入或输出模式

使用流程为:

Step1:获取控制台句柄。

Step2:使用GetConsoleMode函数获取控制台当前的输入输出模式。

Step3:在模式中添加windows系统的一个预定义宏:

ENABLE_VIRTUAL_TERMINAL_PROCESSING,其值为0x0004,该标志的作用:使用 WriteFile 或 WriteConsole 写入时,将为 VT100 和类似控制字符序列分析字符,这些字符序列可控制光标移动、颜色/字体模式以及其他也可通过现有控制台 API 执行的操作。

Step4:在输出的格式串中进行格式设置。

设置格式为:

这里SGR为signature的缩写,这里就是对应<n>的值,其部分定义见下表:

完整表格请参看:

控制台虚拟终端序列

表格太长了,不便全部拷贝过来。做实验。

实验三:C语言改变控制台颜色

为了得到我们要的效果,代码如下:

/* c_change_conclr_v2.c */
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#include <stdio.h>
#include <windows.h>
int main()
{
    // 获取控制台句柄
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    // 获取控制台当前色输入输出模式
    DWORD dwMode = 0;
    if (!GetConsoleMode(hOut, &dwMode))
    {
        return GetLastError();
    }
    // 控制台当前色输入输出模式中添加ENABLE_VIRTUAL_TERMINAL_PROCESSING
    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    if (!SetConsoleMode(hOut, dwMode))
    {
        return GetLastError();
    }
    printf("\x1b[31mThis text has a red foreground using SGR.31.\r\n");
    printf("\x1b[32mThis text has a green foreground using SGR.31.\r\n");
    printf("\x1b[39mThis text has restored the foreground color only.\r\n");
    printf("\x1b[49mThis text has restored the background color only.\r\n");

    return 0;
}

编译执行后,输出为:

这样便改变了字体颜色。为了使用方面,我将它封装为一个头文件。代码如下:

/* console_color.h */
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#define COLOR_FORE_RED      "\x1b[31m"
#define COLOR_FORE_GREEN    "\x1b[32m"
#define COLOR_FOR_DEFAULT   "\x1b[39m"
#define COLOR_BACK_DEFLAUT  "\x1b[49m"
#include <windows.h>
#define CONFIG_CONSOLE_COLOR \
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); \
if (hOut == INVALID_HANDLE_VALUE) \
{ \
    return GetLastError(); \
} \
DWORD dwMode = 0; \
if (!GetConsoleMode(hOut, &dwMode)) \
{ \
    return GetLastError(); \
} \
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; \
if (!SetConsoleMode(hOut, dwMode)) \
{ \
    return GetLastError(); \
}

这样改变字体颜色,只需要引入这个头文件,然后引用这个宏就可以了。如下列程序:

/* change_conclr_macro.c */
#include <stdio.h>
#include "console_color.h" // 引入头文件

int main() 
{
    CONFIG_CONSOLE_COLOR  //  引入宏

    // 每个输出格式串中添加一个格式控制的宏
    printf(COLOR_FORE_RED "This text has a red foreground using SGR.31.\r\n");
    printf(COLOR_FORE_GREEN "This text has a green foreground using SGR.31.\r\n");
    printf(COLOR_FOR_DEFAULT "This text has restored the foreground color only.\r\n");
    printf(COLOR_BACK_DEFLAUT "This text has restored the background color only.\r\n");

    return 0;
}

编译执行后:

六、总结

微软放弃SetConsoleTextAttribute函数的主要原因是跨平台,如下格式

printf("\x1b[31mThis text has a red foreground using SGR.31.\r\n");

就是来源于Unix操作系统。

这玩应想完全实现跨平台,还是有一段路要走。

  • 37
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值