2009-10-02 13 views
41

Jak mogę usunąć bieżącą drukowaną linię konsoli w C? Pracuję nad systemem Linux. Na przykład -Wymaż bieżącą drukowaną linię konsolową.

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

Chcę wydrukować pa na tej samej linii w miejsce powitania.

+0

Biblioteka ncurses prawdopodobnie będzie to zrobić.Nie znam go na tyle dobrze, aby podać odpowiedź, ale tutaj są linki do [docs] (http://linux.die.net/man/3/clrtoeol) i [powiązane pytanie] (http: // stackoverflow .com/questions/5072881). Komentarz ten opiera się na odpowiedzi Wil Shipleya z 2009 roku, którą można usunąć. –

Odpowiedz

48

Można użyć VT100 escape codes. Większość terminali, w tym xterm, ma świadomość VT100. Aby skasować linię, jest to ^[[2K. W C to daje:

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

To nie resetuje kursora do początku linii (przynajmniej w moim terminalu), więc '\ r' jest nadal wymagane. Zauważ, że użycie '% c' nie jest konieczne. Alternatywnie: 'printf (" \ 33 [2K \ r ");' – Stephan202

+3

Troszkę uwagi, że formatowanie ANSI nie jest obsługiwane w systemie Windows ([kod escape ANSI] (http://en.wikipedia.org/wiki/ANGO_katalog_zgórny))). Ale nadal możliwe jest użycie 'printf (" \ r ");'. – CocoaBob

+2

@CocoaBob That [zmieniono] (http://www.nivot.org/blog/post/2016/02/04/Windows-10-TH2- (v1511) -Konsola-Host-Enhancements) od aktualizacji Windows 10 TH2. – Hydranix

50

Można użyć \r (carriage return), aby powrócić kursor na początku wiersza:

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

To będzie drukować bye na tej samej linii. Nie spowoduje to jednak usunięcia istniejących znaków, a ponieważ bye jest krótszy niż witam, otrzymasz numer byelo. Aby skasować go można dokonać nowego drukiem już nadpisać dodatkowe znaki:

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

Albo najpierw usunąć go z kilku pomieszczeń, a następnie wydrukować swój nowy ciąg:

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

To będzie drukować hello, a następnie przejdź do początku wiersza i zastąp go spacją, a następnie wróć do początku ponownie i wydrukuj pa.

3

Zwykle, gdy na końcu napisu znajduje się znak "\ r", drukowany jest tylko powrót karetki bez żadnego znaku nowej linii. Jeśli masz następujące elementy:

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

wyjście będzie:

barooo 

Jedno mogę zasugerować (może obejście) ma mieć NULL zakończone ciąg stałym rozmiarze, który jest zainicjowany do całej przestrzeni znaków, kończąc na '\ r' (za każdym razem przed drukowaniem), a następnie użyj strcpy do skopiowania do niego ciągu znaków (bez nowej linii), więc każdy kolejny wydruk nadpisze poprzedni ciąg znaków. Coś takiego:

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

Można zrobić sprawdzanie błędów, tak aby my_string zawsze jest conajmniej jeden mniej niż str długości, ale masz podstawową ideę.

3

Można usunąć linię używając \ b

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

Czy \ b nie przesuwa kursora o jeden znak? Postaci nadal tam będą. Sądzę, że możesz zrobić coś w rodzaju "\ b \ b". –

+0

W moim Linux-ie wiersz zostanie usunięty. Być może zachowanie jest inne na innych platformach. –

2

i iteruje poprzez słowa tablicy char. j śledzi długość słowa. "\b \b" usuwa słowo podczas kopiowania przez linię.

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

Ten skrypt jest zakodowany dla Twojego przykładu.

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

Kliknij tutaj, aby uzyskać source.

16

Niektóre wartościowe subtelności ...

\33[2K kasuje całą linię kursor znajduje się obecnie na

\033[A przesuwa kursor o jeden wiersz w górę, ale w tej samej kolumnieczyli nie do początku linia

\r przynosi kursor na początku linii (r jest na zwijanie), ale nie wymazać nic

W Xterm w szczególności próbowałem odpowiedzi wymienionych powyżej i jedynym sposobem, w jaki znalazłem, aby usunąć linię i rozpocząć ponownie na początku jest sekwencja (od powyższego komentarza opublikowanego przez @ Stephan202 oraz @vlp i @mantal) \33[2K\r

z uwagą wdrażania, aby zmusić go do prawidłowej pracy na przykład w scenariuszu odliczania ponieważ nie używał nowej linii charakter '\n' na koniec każdego fprintf(), więc musiałem fflush() strumienia za każdym razem (aby podać kontekst, uruchomiłem xterm za pomocą widnokręgu na maszynie z Linuksem bez przekierowania stdout, właśnie pisałem do buforowanego wskaźnika PLIKA fdfile z nieblokującym się deskryptorem pliku, który miałem n adres terminala pseudo który w swoim przypadku był /dev/pts/21)

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

Należy zauważyć, że stosowany zarówno [2K sekwencję \ 33, aby usunąć z linii, po której następuje sekwencja \r nawijania, aby przesunąć kursor na początku linia. Musiałem fflush() po każdym fprintf(), ponieważ nie mam nowego znaku linii na końcu '\n'. Ten sam wynik bez konieczności fflush() wymagałoby dodatkowej sekwencji, aby przejść do wiersza:

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

pamiętać, że jeśli masz coś na linii tuż nad linią chcesz pisać dalej, będzie się nadmiernie napisane przy użyciu pierwszego fprintf(). Trzeba by pozostawić dodatkowy wiersz powyżej w celu umożliwienia pierwszego ruchu w górę jednym wierszu:

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); 
} 
+0

'\ r' jest skrótem od" carriage return ", a nie" rewind "i jest reliktem starych dobrych maszyn do pisania, które miały oddzielne operacje dla" przesunąć kursor z powrotem do pierwszej kolumny "(' \ r') i "poprzednia strona do następnego rzędu "(' \ n'). – ijustlovemath

0

Istnieje prosty trik można pracować tutaj, ale potrzeba przygotowania przed wydrukowaniem, trzeba umieścić co kiedykolwiek chce wydrukować w zmiennej, a następnie wydrukować, abyś znał długość, aby usunąć ciąg. Oto przykład.

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

} 
0

Po prostu znalazłem ten stary wątek, szukając jakiejś sekwencji ucieczki, aby wyzerować aktualną linię.

To całkiem zabawne, że nikt nie wpadł na pomysł (lub ja tęskniłem za tym), że printf zwraca liczbę zapisanych znaków. Więc po prostu wypisz '\ r' + tyle pustych znaków, ile printf zwróciło i dokładnie wypiszesz poprzednio napisany tekst.

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

Ponieważ nie mogę używać VT100, wydaje się, muszę trzymać się tego rozwiązania

Powiązane problemy