Стереть текущую строку печатной консоли

Как стереть текущую напечатанную строку консоли в C? Я работаю на Linux. Например -

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

Я хочу напечатать bye на той же строке вместо hello.

9 ответов


можно использовать VT100 коды эвакуации. Большинство терминалов, включая xterm, знают VT100. Для стирания строки это ^[[2K. В C это дает:

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

можно использовать \r (возврат каретки) для возврата курсора в начало строки:

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

выводит пока на той же строке. Он не будет стирать существующие символы, хотя, и потому что пока меньше, чем привет, вы будете в конечном итоге с byelo. Чтобы стереть его, вы можете сделать новую печать длиннее, чтобы перезаписать дополнительные символы:

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

или, сначала удалите его с помощью несколько пробелов, затем распечатайте новую строку:

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

это будет печатать привет, затем перейдите в начало строки и перепишите ее пробелами, затем снова вернитесь к началу и напечатайте пока.


некоторые полезные тонкости...

[2K стирает всю строку, на которой в данный момент находится курсор

3[A перемещает курсор вверх на одну строку, но в тот же столбец т. е. не до начала строки

\r приводит курсор к началу строки (r для перемотки назад), но не стереть что-нибудь

в xterm конкретно, я попробовал ответы, упомянутые выше, и единственный способ, который я нашел чтобы стереть строку и начать снова в начале, это последовательность (из комментария выше, опубликованного @Stephan202, а также @vlp и @mantal) [2K\r

в примечании к реализации, чтобы заставить его работать правильно, например, в сценарии обратного отсчета, так как я не использовал новый символ строки '\n' в конце каждого fprintf(), поэтому мне пришлось fflush() поток каждый раз (чтобы дать вам некоторый контекст, я начал xterm с помощью вилки на машине linux без перенаправления stdout, я просто запись в буферизованный файл указателя fdfile с неблокирующим файловым дескриптором я сидел на псевдо-терминальном адресе, который в моем случае был /dev/pts/21):

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

обратите внимание, что я использовал последовательность \33[2K для стирания строки, за которой следует \r перемотать последовательность, чтобы переместить курсор в начале строки. Мне пришлось fflush() после каждого fprintf() потому что у меня нет нового символа строки в конце '\n'. Тот же результат без необходимости fflush () требуется дополнительная последовательность, чтобы подняться вверх по строке:

fprintf(fdfile, "3[A[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, "3[A[2KT\rT minus %d seconds...\n", i);
    i--;
    sleep(1);
}

обычно, когда у вас есть "\r " в конце строки, только возврат каретки печатается без какой-либо новой строки. Если у вас есть следующее:

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

выход будет:

barooo

одна вещь, которую я могу предложить (возможно, обходной путь), - это иметь строку фиксированного размера с нулевым завершением, которая инициализируется для всех символов пробела, заканчиваясь на "\r" (каждый раз перед печатью), а затем использовать strcpy для копирования вашей строки в нее (без новой строки), поэтому каждый последующий print перезапишет предыдущую строку. Что-то вроде этого:--6-->

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, но вы получите основную идею.


вы можете удалить строку, используя \b

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

i перебирает слова типа char массив. j отслеживает длину слова. "\b \b" стирает слово во время резервного копирования по линии.

#include<stdio.h>

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

    char words[] = "Hello Bye";

    while(words[i]!='')
    {
        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 <stdio.h>

int main ()
{

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

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

    //remove line
    fputs("3[A3[2K",stdout);
    rewind(stdout);

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

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

}

просто нашел этот старый поток, ища какую-то escape-последовательность, чтобы очистить фактическую строку.

забавно, что никто не пришел к идее (или я ее пропустил), что 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, кажется, я должен придерживаться этого решения