Die ncurses-Bibliothek wird dies wahrscheinlich tun. Ich weiß es nicht gut genug, um eine Antwort zu geben, aber hier sind Links zu Dokumenten und einer verwandten Frage . Dieser Kommentar basiert auf der Antwort von Wil Shipley aus dem Jahr 2009, die möglicherweise gelöscht wird.
Tom Zych
Antworten:
72
Sie können VT100-Escape-Codes verwenden . Die meisten Terminals, einschließlich xterm, sind VT100-fähig. Zum Löschen einer Zeile ist dies ^[[2K. In C ergibt dies:
Dies setzt den Cursor nicht auf den Zeilenanfang zurück (zumindest in meinem Terminal) und \rist daher weiterhin erforderlich. Beachten Sie, dass die Verwendung von %cnicht erforderlich ist. Alternative:printf("\33[2K\r");
Stephan202
5
Nur ein kleiner Hinweis, dass die ANSI-Formatierung in Windows nicht unterstützt wird ( ANSI-Escape-Code ). Aber es ist immer noch möglich zu verwenden printf("\r ");.
CocoaBob
2
@CocoaBob Das hat sich seit dem Windows 10 Update TH2 geändert .
Hydranix
1
Es funktioniert nicht unter Windows vor Windows 10
Phuclv
1
@ LưuVĩnhPhúc - Frage endet mit: Ich arbeite an einem Linux-System
Mouviciel
57
Mit einem \r( Wagenrücklauf ) können Sie den Cursor an den Zeilenanfang zurücksetzen:
printf("hello");
printf("\rbye");
Dies wird Tschüss in derselben Zeile drucken . Die vorhandenen Zeichen werden jedoch nicht gelöscht, und da bye kürzer als hallo ist , erhalten Sie byelo . Um es zu löschen, können Sie Ihren neuen Druck verlängern, um die zusätzlichen Zeichen zu überschreiben:
printf("hello");
printf("\rbye ");
Oder löschen Sie es zuerst mit ein paar Leerzeichen und drucken Sie dann Ihre neue Zeichenfolge:
printf("hello");
printf("\r ");
printf("\rbye");
Das wird Hallo drucken , dann zum Anfang der Zeile gehen und sie mit Leerzeichen überschreiben, dann wieder zum Anfang zurückkehren und Tschüss drucken .
\33[2K löscht die gesamte Zeile, in der sich Ihr Cursor gerade befindet
\033[ABewegt den Cursor um eine Zeile nach oben, jedoch in derselben Spalte, dh nicht zum Zeilenanfang
\rbringt Sie den Cursor von der Zeile an den Anfang (r für Wagenrücklauf NB Zeilenumbrüche enthalten keine Newline so auf der gleichen Linie Cursor bleibt) , aber nicht der Fall ist alles gelöscht ,
Insbesondere in xterm habe ich die oben genannten Antworten ausprobiert. Die einzige Möglichkeit, die Zeile zu löschen und am Anfang erneut zu beginnen, ist die Sequenz (aus dem obigen Kommentar von @ Stephan202 sowie @vlp und @mantal). \33[2K\r
Um die Implementierung zum Beispiel in einem Countdown-Szenario ordnungsgemäß zu gestalten, da ich '\n'
am Ende jedes Zeichens kein neues Zeilenzeichen verwendet fprintf()habe, musste ich fflush()jedes Mal den Stream verwenden (um Ihnen einen Kontext zu geben, habe ich begonnen xterm mit einem Fork auf einem Linux-Rechner ohne Umleitung von stdout schrieb ich gerade in den gepufferten FILE-Zeiger fdfilemit einem nicht blockierenden Dateideskriptor, den ich auf der Pseudo-Terminal-Adresse hatte (in meinem Fall /dev/pts/21):
fprintf(fdfile, "\33[2K\rT minus %d seconds...", i);
fflush(fdfile);
Beachten Sie, dass ich sowohl die \ 33 [2K-Sequenz zum Löschen der Zeile als auch die \rWagenrücklaufsequenz verwendet habe, um den Cursor am Zeilenanfang neu zu positionieren. Ich musste fflush()nach jedem, fprintf()weil ich am Ende kein neues Zeilenzeichen habe '\n'. Das gleiche Ergebnis ohne fflush () würde die zusätzliche Sequenz erfordern, um eine Zeile nach oben zu gehen:
fprintf(fdfile, "\033[A\33[2K\rT minus %d seconds...\n", i);
Beachten Sie, dass wenn Sie etwas in der Zeile unmittelbar über der Zeile haben, in die Sie schreiben möchten, es mit dem ersten fprintf () überschrieben wird. Sie müssten eine zusätzliche Zeile darüber lassen, um die erste Bewegung um eine Zeile nach oben zu ermöglichen:
i = 3;
fprintf(fdfile, "\nText to keep\n");
fprintf(fdfile, "Text to erase****************************\n");
while(i > 0) { // 3 second countdownfprintf(fdfile, "\033[A\33[2KT\rT minus %d seconds...\n", i);
i--;
sleep(1);
}
\rsteht für "Wagenrücklauf", nicht für "Zurückspulen" und ist ein Relikt guter alter Schreibmaschinen, die separate Operationen für "Cursor zurück in die erste Spalte bewegen" ( \r) und "Seite zur nächsten Zeile vorrücken" ( \n) hatten.
Ijustlovemath
1
\33[2Kist ein Weg zu gehen! Beste Problemlösung für mich. Funktioniert auch unter Windows ConEmu
Grzegorz
Aus irgendeinem Grund funktionieren all diese wunderbaren Antworten für mich einfach nicht, wenn ich eine einfache ausführbare C-Datei auf meinem MacOS in Terminal ausführe (bash, zsh was auch immer). Ich bekomme alle möglichen seltsamen Verhaltensweisen und seltsame Zeichen (oder gar nichts) gedruckt, aber niemals die klare Linie als das
Überdrucken
4
Sie können die Zeile mit \ b löschen
printf("hello");
int i;
for (i=0; i<80; i++)
{
printf("\b");
}
printf("bye");
Bewegt \ b den Cursor nicht einfach um ein Zeichen zurück? Die Charaktere werden immer noch da sein. Ich denke du könntest so etwas wie "\ b \ b" machen.
Andre Miller
Auf meiner Linux-Box wird die Zeile gelöscht. Möglicherweise ist das Verhalten auf anderen Plattformen anders.
Alexander Egger
3
Wenn Sie am Ende der Zeichenfolge ein '\ r' haben, wird normalerweise nur der Wagenrücklauf ohne Zeilenumbruch gedruckt. Wenn Sie Folgendes haben:
printf("fooooo\r");
printf("bar");
Die Ausgabe lautet:
barooo
Eine Sache, die ich vorschlagen kann (möglicherweise eine Problemumgehung), besteht darin, eine NULL-terminierte Zeichenfolge mit fester Größe zu haben, die mit allen Leerzeichen initialisiert wird und jedes Mal vor dem Drucken mit einem '\ r' endet, und dann strcpy zum Kopieren Ihrer Zeichenfolge zu verwenden es (ohne die neue Zeile), so dass jeder nachfolgende Druck die vorherige Zeichenfolge überschreibt. Etwas wie das:
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);
Sie können eine Fehlerprüfung durchführen, so dass my_stringimmer mindestens eine Länge weniger als ist str, aber Sie erhalten die Grundidee.
#include<stdio.h>intmain(){
//write some input fputs("hello\n",stdout);
//wait one second to change line above
sleep(1);
//remove linefputs("\033[A\033[2K",stdout);
rewind(stdout);
//write new linefputs("bye\n",stdout);
return0;
}
Es gibt einen einfachen Trick, den Sie hier ausführen können, der jedoch vor dem Drucken vorbereitet werden muss. Sie müssen alles, was Sie drucken möchten, in eine Variable einfügen und dann drucken, damit Sie die Länge zum Entfernen der Zeichenfolge kennen. Es gibt ein Beispiel.
#include<iostream>#include<string> //actually this thing is not nessasory in tdm-gccusingnamespacestd;
intmain(){
//create string variablestring str="Starting count";
//loop for printing numbersfor(int i =0;i<=50000;i++){
//get previous string length and clear it from screen with backspace charactorcout << string(str.length(),'\b');
//create string line
str="Starting count " +to_string(i);
//print the new line in same spotcout <<str ;
}
}
Ich habe gerade diesen alten Thread gefunden und nach einer Art Escape-Sequenz gesucht, um die eigentliche Zeile auszublenden.
Es ist ziemlich lustig, dass niemand auf die Idee gekommen ist (oder ich habe es verpasst), dass printf die Anzahl der geschriebenen Zeichen zurückgibt. Drucken Sie also einfach '\ r' + so viele leere Zeichen, wie printf zurückgegeben hat, und Sie werden den zuvor geschriebenen Text genau leer lassen.
Unter Windows 10 kann der VT100-Stil verwendet werden, indem der VT100-Modus in der aktuellen Konsole aktiviert wird, um Escape-Sequenzen wie folgt zu verwenden:
#include<windows.h>#include<iostream>#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004#define DISABLE_NEWLINE_AUTO_RETURN 0x0008intmain(){
// 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 secondswhile(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 ....";
}
}
Antworten:
Sie können VT100-Escape-Codes verwenden . Die meisten Terminals, einschließlich xterm, sind VT100-fähig. Zum Löschen einer Zeile ist dies
^[[2K
. In C ergibt dies:printf("%c[2K", 27);
quelle
\r
ist daher weiterhin erforderlich. Beachten Sie, dass die Verwendung von%c
nicht erforderlich ist. Alternative:printf("\33[2K\r");
printf("\r ");
.Mit einem
\r
( Wagenrücklauf ) können Sie den Cursor an den Zeilenanfang zurücksetzen:printf("hello"); printf("\rbye");
Dies wird Tschüss in derselben Zeile drucken . Die vorhandenen Zeichen werden jedoch nicht gelöscht, und da bye kürzer als hallo ist , erhalten Sie byelo . Um es zu löschen, können Sie Ihren neuen Druck verlängern, um die zusätzlichen Zeichen zu überschreiben:
printf("hello"); printf("\rbye ");
Oder löschen Sie es zuerst mit ein paar Leerzeichen und drucken Sie dann Ihre neue Zeichenfolge:
printf("hello"); printf("\r "); printf("\rbye");
Das wird Hallo drucken , dann zum Anfang der Zeile gehen und sie mit Leerzeichen überschreiben, dann wieder zum Anfang zurückkehren und Tschüss drucken .
quelle
Einige lohnende Feinheiten ...
\33[2K
löscht die gesamte Zeile, in der sich Ihr Cursor gerade befindet\033[A
Bewegt den Cursor um eine Zeile nach oben, jedoch in derselben Spalte, dh nicht zum Zeilenanfang\r
bringt Sie den Cursor von der Zeile an den Anfang (r für Wagenrücklauf NB Zeilenumbrüche enthalten keine Newline so auf der gleichen Linie Cursor bleibt) , aber nicht der Fall ist alles gelöscht ,Insbesondere in xterm habe ich die oben genannten Antworten ausprobiert. Die einzige Möglichkeit, die Zeile zu löschen und am Anfang erneut zu beginnen, ist die Sequenz (aus dem obigen Kommentar von @ Stephan202 sowie @vlp und @mantal).
\33[2K\r
Um die Implementierung zum Beispiel in einem Countdown-Szenario ordnungsgemäß zu gestalten, da ich
'\n'
am Ende jedes Zeichens kein neues Zeilenzeichen verwendetfprintf()
habe, musste ichfflush()
jedes Mal den Stream verwenden (um Ihnen einen Kontext zu geben, habe ich begonnen xterm mit einem Fork auf einem Linux-Rechner ohne Umleitung von stdout schrieb ich gerade in den gepufferten FILE-Zeigerfdfile
mit einem nicht blockierenden Dateideskriptor, den ich auf der Pseudo-Terminal-Adresse hatte (in meinem Fall/dev/pts/21
):fprintf(fdfile, "\33[2K\rT minus %d seconds...", i); fflush(fdfile);
Beachten Sie, dass ich sowohl die \ 33 [2K-Sequenz zum Löschen der Zeile als auch die
\r
Wagenrücklaufsequenz verwendet habe, um den Cursor am Zeilenanfang neu zu positionieren. Ich musstefflush()
nach jedem,fprintf()
weil ich am Ende kein neues Zeilenzeichen habe'\n'
. Das gleiche Ergebnis ohne fflush () würde die zusätzliche Sequenz erfordern, um eine Zeile nach oben zu gehen:fprintf(fdfile, "\033[A\33[2K\rT minus %d seconds...\n", i);
Beachten Sie, dass wenn Sie etwas in der Zeile unmittelbar über der Zeile haben, in die Sie schreiben möchten, es mit dem ersten fprintf () überschrieben wird. Sie müssten eine zusätzliche Zeile darüber lassen, um die erste Bewegung um eine Zeile nach oben zu ermöglichen:
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); }
quelle
\r
steht für "Wagenrücklauf", nicht für "Zurückspulen" und ist ein Relikt guter alter Schreibmaschinen, die separate Operationen für "Cursor zurück in die erste Spalte bewegen" (\r
) und "Seite zur nächsten Zeile vorrücken" (\n
) hatten.\33[2K
ist ein Weg zu gehen! Beste Problemlösung für mich. Funktioniert auch unter Windows ConEmuSie können die Zeile mit \ b löschen
printf("hello"); int i; for (i=0; i<80; i++) { printf("\b"); } printf("bye");
quelle
Wenn Sie am Ende der Zeichenfolge ein '\ r' haben, wird normalerweise nur der Wagenrücklauf ohne Zeilenumbruch gedruckt. Wenn Sie Folgendes haben:
printf("fooooo\r"); printf("bar");
Die Ausgabe lautet:
Eine Sache, die ich vorschlagen kann (möglicherweise eine Problemumgehung), besteht darin, eine NULL-terminierte Zeichenfolge mit fester Größe zu haben, die mit allen Leerzeichen initialisiert wird und jedes Mal vor dem Drucken mit einem '\ r' endet, und dann strcpy zum Kopieren Ihrer Zeichenfolge zu verwenden es (ohne die neue Zeile), so dass jeder nachfolgende Druck die vorherige Zeichenfolge überschreibt. Etwas wie das:
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);
Sie können eine Fehlerprüfung durchführen, so dass
my_string
immer mindestens eine Länge weniger als iststr
, aber Sie erhalten die Grundidee.quelle
i
iteriert durch char-Array-Wörter.j
Verfolgt die Wortlänge."\b \b"
löscht das Wort beim Zurücksetzen über die Zeile.#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; }
quelle
Dieses Skript ist für Ihr Beispiel fest codiert.
#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; }
Klicken Sie hier für die Quelle .
quelle
Es gibt einen einfachen Trick, den Sie hier ausführen können, der jedoch vor dem Drucken vorbereitet werden muss. Sie müssen alles, was Sie drucken möchten, in eine Variable einfügen und dann drucken, damit Sie die Länge zum Entfernen der Zeichenfolge kennen. Es gibt ein Beispiel.
#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 ; } }
quelle
Ich habe gerade diesen alten Thread gefunden und nach einer Art Escape-Sequenz gesucht, um die eigentliche Zeile auszublenden.
Es ist ziemlich lustig, dass niemand auf die Idee gekommen ist (oder ich habe es verpasst), dass printf die Anzahl der geschriebenen Zeichen zurückgibt. Drucken Sie also einfach '\ r' + so viele leere Zeichen, wie printf zurückgegeben hat, und Sie werden den zuvor geschriebenen Text genau leer lassen.
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; }
Da ich VT100 nicht verwenden kann, muss ich mich anscheinend an diese Lösung halten
quelle
echo -e "hello\c" ;sleep 1 ; echo -e "\rbye "
Was der obige Befehl bewirkt:
Es wird Hallo gedruckt und der Cursor bleibt auf "o" (mit \ c)
Dann wartet es 1 Sekunde (Schlaf 1)
Dann wird Hallo durch Tschüss ersetzt (mit \ r)
NOTE : Using ";", We can run multiple command in a single go.
quelle
Unter Windows 10 kann der VT100-Stil verwendet werden, indem der VT100-Modus in der aktuellen Konsole aktiviert wird, um Escape-Sequenzen wie folgt zu verwenden:
#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 ...."; } }
siehe folgenden Link: Windows VT100
quelle