【问题标题】:Colored terminal output does not reset彩色终端输出不复位
【发布时间】:2015-07-15 14:29:16
【问题描述】:

在编写更大的程序时,我偶然发现了彩色文本输出的一个小问题。这是一个更简单的程序,可以重现此问题。

#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;
}

现在这是我运行程序时得到的结果:

第一次 - 预期结果

第二次

如您所知,即使在将颜色重置为红色后,程序仍会随机打印行。在新终端中启动时,它总是打印预期的结果。除非我运行clear,否则无法保证输出不会像第二张图片那样被破坏。

在图片中我使用的是xterm,虽然其他终端做同样的事情。

我能做些什么来防止这种情况发生?

【问题讨论】:

  • 终端开始滚动时会不会出现问题?
  • 好点。看起来确实如此。

标签: c linux terminal stdio


【解决方案1】:

正如所评论的,这是一些知名终端的已知行为:滚动(或反向滚动)时,屏幕上新清除的区域将填充当前背景颜色。 Linux 控制台会执行此操作(除了几年前出现的故障,在 terminal database 中注明)。 xterm does it.

在 ncurses 中,几个相关的行为被归为bce(背景颜色擦除)功能:

  • 由于滚动而填充新清除的行
  • 擦除显示,以及擦除以光标结尾或开头的部分。
  • 删除一行,或删除从光标到行尾的部分
  • 在光标位置插入(空白)
  • 删除字符

通常 ncurses 会填充空白(只有当终端条目为 poorly chosen 时才会出现问题),您不会看到这一点。但是使用简单的转义序列意味着您可以稍微探索bce 的细微差别。

直接使用转义序列打印颜色的终端应用程序应在写入任何其他不打算着色的文本之前重置颜色。其他应用程序(例如 shell 中的行编辑)在擦除行内的文本时必须牢记此规则

【讨论】:

    【解决方案2】:

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

    使用:

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

    输出:

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

    使用:

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

    输出:

    您也可以考虑创建一个宏以将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 的答案。
    • 这并不能真正解释转义十六进制与转义八进制。
    • 在 OP 的 cmets 中,我们得出结论,当程序导致终端滚动时,终端的行为很奇怪。这就是为什么转义码应该在行尾而不是开头的原因。
    • 我明白,但是,我不相信这正是正在发生的事情。如果您查看以上两个实例的结果,则会在换行符之后设置转义码。但是,在转义码具有八进制表示的情况下,它可以工作。
    • 我想通了。瘸。如果它有一个前导0,它将不会除了十六进制值。如0x1bx1b。请注意,这是使用 iTerm2。
    【解决方案3】:

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

    问题可能是由于puts 附加了换行符造成的。使用printf修改您的代码。

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

    【讨论】:

    • 我尝试了同样的事情并想回复答案,但您似乎领先于我。无论如何,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    • 2011-05-15
    • 1970-01-01
    相关资源
    最近更新 更多