我正在try 制作一个CLI,使用READLINE突出显示文本.但是,每当我将该行替换为包含ANSI代码的字符串以便它可以着色时,就会显示ANSI代码,而不是显示为彩色的行.我如何才能使文本显示为彩色?

每次输入字符时,我都会使用备用界面将该行替换为突出显示的文本.

下面是一个使用的最小示例,它只用红色的文本RED替换所有键入的内容(使用ANSI代码\e[0;31m)

#include <stdio.h>
#include <readline/readline.h>

void linehandler(char *line) { printf("line: %s\n", line); }

int main() {
  rl_callback_handler_install("> ", linehandler);
  while (1) {
    rl_callback_read_char();
    rl_replace_line("\e[0;31mRED\e[0m", 0);
    rl_redisplay();
  }
}

我得到的输出是:

> ^[[0;31mRED^[[0m
line: RED

请注意,在第二行,文本RED显示为红色,不出所料.然而,第一行是无色的.

我try 将ANSI代码包装在\001\002中(我使用"\001\e[0;31m\002RED\001\e[0m\002"呼叫replace_line).这并没有帮助,ANSI代码仍然出现,以及^A^B.

如何使文本显示为彩色而不是打印的ANSI代码?我应该try 一种完全不同的方法吗?

推荐答案

这里要编辑的内容不是输出行,它被发送到stdout,所以发送到打印机,并使用一些转义代码指示打印机元信息(粗体等). 您正在编辑的是来自键盘的输入行.

当然,现在(至少在过go 50年)没有实际涉及的打印机,模拟它的设备就是模拟输入的设备(终端;甚至终端模拟器,这是一个"控制台窗口",它是一个打印机模拟器模拟器).但是,这些ANSI代码是用于输出而不是用于输入.

当然,这是Readline.因此,在现实中,它只是模仿上述行为,并像打印其他输出一样打印rl_buffer_line.但即使是这样,该模拟也是正确完成的,转义字符不会被解释.此外,它不一定会打印所有内容.

这更像是逆向工程,而不是我在文档中读到的东西,所以我不知道你能指望多少.但显然,rl_replace_line只重写了更改的行的一部分(可能是因为READLINE很旧,可以追溯到物理终端时代,以9600 bps的速度打印东西).因此,在这种速度下,或者在没有这种速度的情况下,您不希望每次更改一行中的一个字节就重写整个行).

请看这段代码(我的tmp[100]很难看,也不安全,但这只是一个实验)

#include <stdio.h>
#include <string.h>

#include <readline/readline.h>
#include <readline/history.h>

void linehandler(char *line)
{
  printf("line: %s\n", line);
}

int main()
{
  rl_callback_handler_install("> ", linehandler);
  while (1)
  {
    rl_callback_read_char();
    char tmp[100]={0};
    if(strstr(rl_line_buffer, "red")){
        printf("\033[31m");
        fflush(stdout);
        strcpy(tmp, rl_line_buffer);
        for(int i=5; rl_line_buffer[i] && i<10; i++) tmp[i] = toupper(rl_line_buffer[i]);
        rl_replace_line(tmp, 0);
    }

    rl_redisplay();
  }
}

因此,它所做的就是扫描当前输入行中的三个字母red.如果它找到它,它会暂时将 colored颜色 更改为红色(请注意,这只需通过printf发送ANSI控制即可完成.因此,Readline完全没有意识到这一点.所以它不会弄乱输入的唯一原因是因为我只打印那些控件,所以它们在屏幕上根本不会占据任何位置).

此外,一旦找到这个red,它还会将5到10个字符大写.

如果您输入one two three red four,您将看到输入字符串确实被更改为one tWO THree red four.WO THfour是红色的. 因此,只有在终端切换到红色后才会发生变化.

证明one tree red甚至在 colored颜色 切换之前就没有重印过.rl_replace_line只印了WO TH张.当我打字的时候four已经打印出来了.

你甚至可以看到,如果你用箭头键回到,改变,比方说onen乘以X,所说的X(但没有其他)也将是红色的.

问题是,您在屏幕上看到的输入并不是真正打印为字符串,而是一个字符一个字符地打印.

现在,我不知道你到底想做什么.很难给出另一种方法的建议.是否只突出显示输入的一部分?还是在一定条件下突出整条线?

但我的小实验给了你一些可能的解决方案. 这是向终端发送控制字符(通过printfputswrite,而不是读取线),因此控制读取线以哪种 colored颜色 打印它打印的future 的东西.

而且,如果你不介意依赖一种没有记录的行为(嗯,也许它是有记录的.只是我通过逆向工程知道了这一点.而且,毕竟,打印ESC[33m个代码也是UB:你不确定打印设备是否理解这一点),你甚至可以欺骗readline只打印一个红色的单词

请参见以下代码:

#include <stdio.h>
#include <string.h>

#include <readline/readline.h>
#include <readline/history.h>

void linehandler(char *line)
{
  printf("line: %s\n", line);
}

int main()
{
  rl_callback_handler_install("> ", linehandler);
  while (1)
  {
    rl_callback_read_char();
    char tmp[100]={0};
    strcpy(tmp, rl_line_buffer);
    for(int i=0; tmp[i] && i<97; i++){
        if(!strncmp(tmp+i, "red", 3)){
            memcpy(tmp+i, "XXX", 3);
            rl_replace_line(tmp, 0);
            rl_redisplay();
            printf("\033[31m"); fflush(stdout);
            memcpy(tmp+i, "red", 3);
            rl_replace_line(tmp, 0);
            rl_redisplay();
            printf("\033[m"); fflush(stdout);
        }
    }

    rl_redisplay();
  }
}

它在输入行中以红色打印单词"red".

请注意,它不会影响行本身(缓冲区).因此,如果您希望该行实际包含ANSI代码,和/或在验证时以红色打印,则必须在linehandler中处理这一问题.

C++相关问答推荐

变量的const视图是否定义良好?

C/C++中的状态库

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

Mise()在虚拟内存中做什么?

以前版本的tty_ldisc_ops.ioctl()是否也需要文件参数?

C:fopen是如何实现二进制模式和文本模式的?

Can函数指针指向C++中具有不同参数连续性的函数

自定义变参数函数的C预处置宏和警告 suppress ?

VS代码';S C/C++扩展称C23真关键字和假关键字未定义

GCC不顾-fno-Builtin-SINCOS旗帜向SINCOS发出呼唤

加密解密工作正常,但返回错误0x80090005

用C语言计算文本文件中的整数个数

为什么函数是按照定义的顺序执行的,而不是按照从avr-c中的int main()调用的顺序执行的?

强制GCC始终加载常量(即只读),即使启用了优化

为什么GCC 13没有显示正确的二进制表示法?

Makefile无法将代码刷新到ATmega328p

如何解释数组中的*(ptr)和*(ptr+2)?

按字典顺序打印具有给定字符的所有可能字符串

在git补丁中自动添加C的宏

Ubuntu编译:C中的文件格式无法识别错误