C语言 为什么第一次调用函数时 ANSI 打印代码,第二次打印颜色?



我正在尝试更改连接 4 游戏中 printf 语句的颜色输出。我做了一个功能来设置打印颜色并重置它。它适用于我的大部分代码,但不适用于第一次调用时调用的第一个函数,但从那里开始工作。有问题的函数是主程序中调用的第一个函数。

我尝试了函数的不同定位,将其定位在调用函数的两侧,并将颜色代码直接输入到 printf 函数中,但它总是第一次失败(而且只有第一次(。

#include <stdio.h>
void sprint_green();
void sprint_red();
int main_menu ( void ){
int a = 0;
char opt [20];
sprint_red();
printf("============================n");
printf("    Welcome to Connect4!n");
printf("============================n");
sprint_reset();
// Continue asking for an option until a valid option (n/l/q) is entered
while (a == 0)
{
sprint_green();
printf("(N)ew gamen(L)oad gamen(Q)uit");
sprint_reset();
fgets(opt, 20, stdin);
// if 'n', return 1
if(opt[0] == 'n' || opt[0] == 'N'){
a = 1;
}
// if 'l', return 2
else if(opt[0] == 'l' || opt[0] == 'L'){
a = 2;
}
// if 'q', return -1
else if(opt[0] == 'q' || opt[0] == 'Q'){
a = -1;
printf("nGoodbye!n");
}
// if anything else, give error message and ask again..
else
{
printf("Invalid optionn");
}
}
system("cls");
return(a);
}
int main (void)
{
int i;
for(i = 0; i < 5; i++)
{
main_menu();
}
}
void sprint_green()
{
printf("33[1;32m");
}
void sprint_red()
{
printf("33[1;31m");
}
void sprint_reset()
{
printf("33[0m");
}

前三个 printf 语句预计将以红色打印,下一个语句将以绿色打印。然而,第一次它被称为打印ANSI颜色代码。

[1;31m============================
Welcome to Connect4!
============================
[0m[1;32m(N)ew game
(L)oad game
(Q)uit[0m

但是,在运行游戏一次并重新启动(不关闭终端(后,这些功能按预期工作。

默认情况下,Windows 命令外壳不会启动 vt100 仿真。正如@Bodo在他的回答中指出的那样,这可以通过运行 shell 命令cls来触发。但是,从技术上讲,shell 命令不需要cls,甚至根本不需要是有效的命令。您只需一个空的system(" ")调用即可触发它!这也是可移植的,因为它除了暂时启动 shell 实例并终止它之外什么都不做。因此,它在Windows或Linux环境中应该同样可以正常工作。

使用修复程序编写代码:

#include <stdio.h>
#include <stdlib.h> // for system()
void sprint_green();
void sprint_red();
int main_menu ( void ){
int a = 0;
char opt [20];
system(" "); // Trigger ANSI emulation
sprint_red();
printf("============================n");
printf("    Welcome to Connect4!n");
printf("============================n");
sprint_reset();
// Continue asking for an option until a valid option (n/l/q) is entered
while (a == 0)
{
sprint_green();
printf("(N)ew gamen(L)oad gamen(Q)uit");
sprint_reset();
fgets(opt, 20, stdin);
// if 'n', return 1
if(opt[0] == 'n' || opt[0] == 'N'){
a = 1;
}
// if 'l', return 2
else if(opt[0] == 'l' || opt[0] == 'L'){
a = 2;
}
// if 'q', return -1
else if(opt[0] == 'q' || opt[0] == 'Q'){
a = -1;
printf("nGoodbye!n");
}
// if anything else, give error message and ask again..
else
{
printf("Invalid optionn");
}
}
system("cls");
return(a);
}
int main (void)
{
int i;
for(i = 0; i < 5; i++)
{
main_menu();
}
}
void sprint_green()
{
printf("33[1;32m");
}
void sprint_red()
{
printf("33[1;31m");
}
void sprint_reset()
{
printf("33[0m");
}

system("cls");工作,但它会清除屏幕。 但是,如果您在启动时只调用它一次,它似乎可以针对整个应用程序修复它

根据
https://solarianprogrammer.com/2019/04/08/c-programming-ansi-escape-codes-windows-macos-linux-terminals/

https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences?redirectedfrom=MSDN,Windows
控制台必须进入ANSI转义模式才能处理转义序列而不是打印它们。

显然,执行cls命令似乎可以做到这一点。

您可以使用GetConsoleMode()以编程方式设置终端模式,并通过设置标志SetConsoleMode()ENABLE_VIRTUAL_TERMINAL_PROCESSING

从引用的Microsoft页复制的代码示例:

#include <stdio.h>
#include <wchar.h>
#include <windows.h>
int main()
{
// Set output mode to handle virtual terminal sequences
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();
}
// Try some Set Graphics Rendition (SGR) terminal escape sequences
wprintf(L"x1b[31mThis text has a red foreground using SGR.31.rn");
wprintf(L"x1b[1mThis text has a bright (bold) red foreground using SGR.1 to affect the previous color setting.rn");
/* ... more examples removed */
return 0;
}

显然,此解决方案仅适用于Windows,但运行system("cls")也是不可移植的。

最新更新