彩色终端输出不会重置

5

在编写一个较大的程序时,我遇到了一个彩色文本输出的小问题。这里有一个更简单的程序,可以重现这个问题。

#include <stdio.h>

#define COL_RESET "\033[0m"
#define COL_BG_RED  "\x1B[41m"

char *str = "the quick brown fox jumped over the lazy dog";

int main(int argc, char *argv[])
{
    int i = 10;
    while (i) {
        puts(COL_BG_RED);
        puts(str);
        puts(COL_RESET);
        puts(str);
        i--;
    }
    return 0;
}

现在运行程序时我得到了以下结果:

第一次 - 预期的结果

first time

第二次

enter image description here

可以看出,即使将颜色重置为红色,该程序仍然会随机打印行。当在新终端中启动时,它总是打印预期的结果。除非运行clear,否则无法保证输出不会像第二张图片那样混乱。

在图片中,我使用的是xterm,尽管其他终端也会出现同样的问题。

我该怎么做才能防止这种情况发生?


1
当终端开始滚动时,问题是否可能开始出现? - huysentruitw
好观点。实际上似乎是这种情况。 - Alexguitar
3个回答

3

似乎问题出现在终端开始滚动时。

问题可能是由于puts附加了一个换行符所致。通过使用printf修改您的代码。

printf(COL_BG_RED);
printf(str);
puts(COL_RESET);
puts(str);

1
我尝试了同样的事情,并想回复你的答案,但似乎你比我更快。不管怎样,非常感谢你。 - Alexguitar

3
如前所述,这是一些知名终端的已知行为:当滚动(或反向滚动)时,屏幕上新清除的区域将填充当前背景颜色。Linux控制台会这样做(除了几年前的一个小故障,在终端数据库中有记录)。xterm也会这样做
在ncurses中,将几个相关行为合并为bce(背景颜色擦除)特性:
  • 由于滚动而填充新清除的行
  • 擦除显示,以及擦除以光标结尾或开头的部分。
  • 擦除一行或擦除从光标到行尾或从行首到光标的部分
  • 在光标位置插入(空格)
  • 删除字符
通常情况下,ncurses会填充空白(只有在终端条目选择不当时才会出现问题),您不会看到这一点。但使用普通的转义序列意味着您可以稍微探索一下bce的细微差别。
直接使用转义序列来打印颜色的终端应用程序在编写任何不带颜色的其他文本之前应该重置颜色。其他应用程序(例如Shell中的行编辑)在擦除行内文本时也必须记住这个规则。

0

当我在本地运行时,我也观察到一些奇怪的行为。

使用:

#define COL_RESET      "\0x1b[39;49m" // reset fore/back ground to normal
#define COL_BG_RED     "\033[41m"

输出:

enter image description here

当我将我的#define从十六进制改为八进制时,我得到了不同的(预期)结果。

使用:

#define COL_RESET "\033[39;49m"

输出:

enter image description here

另外,您可以考虑创建一个宏来使用带颜色的printf

#include <stdio.h>

#define COL_RESET      "\033[39;49m"
#define COL_BG_RED     "\033[41m"
#define COL_BG_NORMAL  "\033[49m"

#define COLOR_NORMAL    "\033[m"
#define COLOR_RESET     "\033[0m"
#define COLOR_BLACK     "\033[30m"
#define COLOR_RED       "\033[31m"
#define COLOR_GREEN     "\033[32m"
#define COLOR_YELLOW    "\033[33m"
#define COLOR_BLUE      "\033[34m"
#define COLOR_MAGENTA   "\033[35m"
#define COLOR_CYAN      "\033[36m"
#define COLOR_WHITE     "\033[37m"

#define COLOR_PRINTF(colorCode,fmt,...) printf("%s" fmt "%s", colorCode, __VA_ARGS__, COL_RESET)

char *str = "the quick brown fox jumped over the lazy dog";

int main(int argc, char *argv[])
{
    int i = 1;
    while (i) {
        COLOR_PRINTF(COLOR_GREEN, "%s\n", str);
        COLOR_PRINTF(COL_BG_RED, "%s\n", str);
        i--;
    }
    return 0;
}

终端可能不喜欢在新行的开头直接使用转义码。请尝试使用Wouter的答案。 - Alexguitar
这并没有真正解释转义十六进制与转义八进制的区别。 - bentank
在对原帖的评论中,我们得出结论,当程序导致终端滚动时,终端的行为变得奇怪。这就是为什么转义码应该放在一行的末尾而不是开头的原因。 - Alexguitar
我理解,但我并不认为这正是发生的事情。如果您查看我的结果,两个实例都会在换行符后设置转义代码。然而,在八进制表示法中具有转义代码的情况下,它可以工作。 - bentank
我搞定了。很糟糕。如果十六进制值有前导“0”,它将不会被接受。例如0x1bx1b。这是使用iTerm2的。 - bentank
"\x" 是在 C 语言源代码中将十六进制转义序列放入字符串的方法。它与终端无关,终端不关心程序源代码的样子,只关心输出结果。 - user2404501

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接