繁体   English   中英

擦除当前打印的控制台行

[英]Erase the current printed console line

如何擦除 C 中当前打印的控制台行? 我正在使用 Linux 系统。 例如 -

printf("hello");
printf("bye");

我想在同一行打印 bye 来代替 hello。

您可以使用VT100 转义码 大多数终端,包括 xterm,都支持 VT100。 对于擦除一行,这是^[[2K 在 C 中,这给出:

printf("%c[2K", 27);

您可以使用\\r回车)将光标返回到行首:

printf("hello");
printf("\rbye");

这将在同一行打印再见 但是它不会删除现有的字符,因为byehello短,所以你最终会得到byelo 要擦除它,您可以延长新打印的时间以覆盖额外的字符:

printf("hello");
printf("\rbye  ");

或者,先用几个空格擦除它,然后打印你的新字符串:

printf("hello");
printf("\r          ");
printf("\rbye");

这将打印hello ,然后转到该行的开头并用空格覆盖它,然后再次返回开头并打印bye

一些有价值的微妙之处......

\\33[2K擦除光标当前所在的整行

\\033[A将光标向上移动一行,但在同一列中,即不移动到行首

\\r将光标移至行首(r 用于回车 NB 回车不包括换行符,因此光标保留在同一行上)但不会擦除任何内容

特别是在 xterm 中,我尝试了上面提到的回复,我发现擦除行并从头开始的唯一方法是序列(来自@Stephan202 以及@vlp 和@mantal 发布的上述评论) \\33[2K\\r

在实现说明中,为了让它在倒计时场景中正常工作,因为我没有在每个fprintf()的末尾使用换行符'\\n' ,所以我不得不fflush()每个流时间(为了给你一些上下文,我在 Linux 机器上使用 fork 启动了 xterm,没有重定向标准输出,我只是用非阻塞文件描述符写入缓冲的 FILE 指针fdfile ,我坐在伪终端地址上,在我的案例是/dev/pts/21 ):

fprintf(fdfile, "\33[2K\rT minus %d seconds...", i);
fflush(fdfile);

请注意,我同时使用了 \\33[2K 序列来擦除该行,然后使用\\r回车序列将光标重新定位在该行的开头。 我不得不在每个fprintf() fflush()之后fflush() ,因为我在'\\n'末尾没有换行符。 不需要 fflush() 的相同结果将需要额外的序列上升一行:

fprintf(fdfile, "\033[A\33[2K\rT minus %d seconds...\n", i);

请注意,如果您在要写入的行正上方的行上有内容,它将被第一个 fprintf() 覆盖。 您必须在上面多留一行,以便第一次向上移动一行:

i = 3;
fprintf(fdfile, "\nText to keep\n");
fprintf(fdfile, "Text to erase****************************\n");
while(i > 0) { // 3 second countdown
    fprintf(fdfile, "\033[A\33[2KT\rT minus %d seconds...\n", i);
    i--;
    sleep(1);
}

您可以使用 \\b 删除该行

printf("hello");
int i;
for (i=0; i<80; i++)
{
  printf("\b");
}
printf("bye");

通常,当字符串末尾有一个 '\\r' 时,只会打印回车而没有任何换行符。 如果您有以下情况:

printf("fooooo\r");
printf("bar");

输出将是:

barooo

我可以建议的一件事(可能是一种解决方法)是有一个以 NULL 终止的固定大小字符串初始化为所有空格字符,以 '\\r' 结尾(每次打印之前),然后使用 strcpy 将您的字符串复制到它(没有换行符),所以每次后续打印都会覆盖前一个字符串。 像这样的东西:

char str[MAX_LENGTH];        
// init str to all spaces, NULL terminated with character as '\r'
strcpy(str, my_string);       // copy my_string into str
str[strlen(my_string)] = ' '; // erase null termination char
str[MAX_LENGTH - 1] = '\r';
printf(str);

您可以进行错误检查,以便my_string长度始终至少比str少 1,但您已了解基本概念。

此脚本是针对您的示例进行硬编码的。

#include <stdio.h>

int main ()
{

    //write some input  
    fputs("hello\n",stdout);

    //wait one second to change line above
    sleep(1);

    //remove line
    fputs("\033[A\033[2K",stdout);
    rewind(stdout);

    //write new line
    fputs("bye\n",stdout);

    return 0;
}

单击此处获取源代码

i遍历字符数组单词。 j跟踪字长。 "\\b \\b"在回退行时擦除单词。

#include<stdio.h>

int main()
{
    int i = 0, j = 0;

    char words[] = "Hello Bye";

    while(words[i]!='\0')
    {
        if(words[i] != ' ') {
            printf("%c", words[i]);
        fflush(stdout);
        }
        else {
            //system("ping -n 1 127.0.0.1>NUL");  //For Microsoft OS
            system("sleep 0.25");
            while(j-->0) {
                printf("\b \b");
            }
        }

        i++;
        j++;
    }

printf("\n");                   
return 0;
}

有一个简单的技巧,您可以在这里工作,但在打印之前需要准备,您必须将想要打印的内容放入变量中,然后打印,以便您知道删除字符串的长度。这是一个示例。

#include <iostream>
#include <string> //actually this thing is not nessasory in tdm-gcc

using namespace  std;

int main(){

//create string variable

string str="Starting count";

//loop for printing numbers

    for(int i =0;i<=50000;i++){

        //get previous string length and clear it from screen with backspace charactor

        cout << string(str.length(),'\b');

        //create string line

        str="Starting count " +to_string(i);

        //print the new line in same spot

        cout <<str ;
    }

}

刚刚找到这个旧线程,寻找某种转义序列来空白实际行。

有趣的是没有人想到(或者我已经错过了) printf 返回写入的字符数。 因此,只需打印 '\\r' + 与 printf 返回的空白字符一样多的空白字符,您将完全空白先前写入的文本。

int BlankBytes(int Bytes)
{
                char strBlankStr[16];

                sprintf(strBlankStr, "\r%%%is\r", Bytes);
                printf(strBlankStr,"");

                return 0;
}

int main(void)
{
                int iBytesWritten;
                double lfSomeDouble = 150.0;

                iBytesWritten = printf("test text %lf", lfSomeDouble);

                BlankBytes(iBytesWritten);

                return 0;
}

由于我无法使用 VT100,看来我必须坚持使用该解决方案

echo -e "hello\c" ;sleep 1 ; echo -e "\rbye  "

上面的命令会做什么:

  1. 它将打印 hello 并且光标将保持在“o”(使用 \\c)

  2. 然后它将等待 1 秒(睡眠 1)

  3. 然后它将用再见替换你好。(使用\\ r)

NOTE : Using ";", We can run multiple command in a single go.

在 Windows 10 下,可以通过在当前控制台中激活 VT100 模式来使用 VT100 样式以使用转义序列,如下所示:

#include <windows.h>
#include <iostream>

#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#define DISABLE_NEWLINE_AUTO_RETURN  0x0008

int main(){

  // enabling VT100 style in current console
  DWORD l_mode;
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  GetConsoleMode(hStdout,&l_mode)
  SetConsoleMode( hStdout, l_mode |
            ENABLE_VIRTUAL_TERMINAL_PROCESSING |
            DISABLE_NEWLINE_AUTO_RETURN );

  // create a waiting loop with changing text every seconds
  while(true) {
    // erase current line and go to line begining 
    std::cout << "\x1B[2K\r";
    std::cout << "wait a second .";
    Sleep(1);
    std::cout << "\x1B[2K\r";
    std::cout << "wait a second ..";
    Sleep(1);
    std::cout << "\x1B[2K\r";
    std::cout << "wait a second ...";
    Sleep(1);
    std::cout << "\x1B[2K\r";
    std::cout << "wait a second ....";
 }

}

请参阅以下链接: windows VT100

其他人已经回答了 OP 的问题。 对于那些想知道为什么回车在他们的 Linux 机器上的行为方式的人来说,这是一个答案 -

回车符的行为似乎与平台有关。

来自 C11 标准的“5.2.2 字符显示语义”部分:

\\r (回车) 将活动位置移动到当前行的初始位置。

来自 POSIX 标准 ( https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html ) 的“3.86 Carriage-Return Character (<回车符>)”部分:

输出流中的一个字符,指示打印应从发生回车的同一物理行的开头开始。 它是C语言中'\\r'指定的字符。 未指定该字符是否是系统传输到输出设备以完成移动到行首的确切序列。

它没有说明回车是否应该擦除(=> 用 NUL 字符填充)整行。 我的猜测是它不应该擦除。

但是,在我的 Linux 机器上(在 x86_64 和 ARM32 上都尝试过),我观察到的是回车符将光标移动到当前行的开头,并且还用 '\\0' 字符(NUL 字符)填充了该行。 为了注意那些 NUL 字符,您可能必须直接从代码中调用 write 系统调用,而不是通过 glibc printf 调用。

我们以下面的代码片段为例:

printf("hello");
printf("\rbye");

在 beaglebone black(32 位 ARM)bash 终端上构建和运行它:

ubuntu@arm:~$ ./a.out 
byeubuntu@arm:~$ 

写入系统调用的 strace 输出:

bye)               = 9 9hello
+++ exited with 4 +++

对我来说,这段代码在 Tera Term VT 控制台上适用于串行控制台 window 和 arduino:

SEROUT.print("\e[A\r\n\e[2K");
SEROUT.print('>');

我使用 '>' 因为在我的控制台命令上我在 '>' 之后键入命令

使用这个 function 清除 C++ 中的n

void clear_line(int n) {
    std::string line_up = "\x1b[A";
    std::string line_clear = "\33[2K\r";
    for (int i = 0; i < n; ++i)
        std::cout << line_up << line_clear << std::flush;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM