Wie zeige ich eine Fortschrittsanzeige in reinem C / C ++ (cout / printf) an?

78

Ich schreibe ein Konsolenprogramm in C ++, um eine große Datei herunterzuladen. Ich kenne die Dateigröße und starte einen Arbeitsthread zum Herunterladen. Ich möchte eine Fortschrittsanzeige anzeigen, damit es kühler aussieht.

Wie werden verschiedene Zeichenfolgen zu unterschiedlichen Zeiten, aber an derselben Position in cout oder printf angezeigt?

xmllmx
quelle
2
Kasse der PDCurses Bibliothek pdcurses.sourceforge.net
Lefteris
2
C ++ Console Progress Indicator könnte helfen
David L.
2
Das Laichen eines wgetProzesses ist keine Option?
Alexandre C.
curses ... ncurses
hippietrail

Antworten:

54

Sie können einen "Wagenrücklauf" (\ r) ohne Zeilenvorschub (\ n) verwenden und hoffen, dass Ihre Konsole das Richtige tut.

James Curran
quelle
30
+ manuelle Spülung, sonst wird es nicht sofort angezeigt, da die Ausgabe gepuffert ist.
Leemes
Und wenn der Benutzer versehentlich die Eingabetaste drückt, bricht es zusammen :( Abgesehen davon ist es vielleicht die portabelste Lösung, +1.
Ali
@Ali Um dies zu vermeiden, müssten Sie das Echo deaktivieren (siehe man termios)
Leemes
1
@leemes #include <termios.h>, versuche das unter M $ Windows :) Wie auch immer, danke für den Tipp, ich werde das wahrscheinlich unter Linux versuchen.
Ali
1
@Ali Es könnte ein Äquivalent für W1ndOw $ geben, aber ich weiß es nicht. ;)
Leemes
104

Verwenden Sie bei einer festen Breite Ihrer Ausgabe Folgendes:

float progress = 0.0;
while (progress < 1.0) {
    int barWidth = 70;

    std::cout << "[";
    int pos = barWidth * progress;
    for (int i = 0; i < barWidth; ++i) {
        if (i < pos) std::cout << "=";
        else if (i == pos) std::cout << ">";
        else std::cout << " ";
    }
    std::cout << "] " << int(progress * 100.0) << " %\r";
    std::cout.flush();

    progress += 0.16; // for demonstration only
}
std::cout << std::endl;

http://ideone.com/Yg8NKj

[>                                                                     ] 0 %
[===========>                                                          ] 15 %
[======================>                                               ] 31 %
[=================================>                                    ] 47 %
[============================================>                         ] 63 %
[========================================================>             ] 80 %
[===================================================================>  ] 96 %

Beachten Sie, dass diese Ausgabe eine Zeile untereinander angezeigt wird , aber in einem Terminalemulator (ich denke auch in der Windows-Befehlszeile) in derselben Zeile gedruckt wird .

Vergessen Sie am Ende nicht, eine neue Zeile zu drucken, bevor Sie weitere Inhalte drucken.

Wenn Sie die Leiste am Ende entfernen möchten, müssen Sie sie mit Leerzeichen überschreiben, um beispielsweise etwas kürzeres zu drucken "Done.".

Das gleiche kann natürlich auch printfin C gemacht werden; Das Anpassen des obigen Codes sollte einfach sein.

Leemes
quelle
35

Für eine CLösung mit einer einstellbaren Breite des Fortschrittsbalkens können Sie Folgendes verwenden:

#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
#define PBWIDTH 60

void printProgress(double percentage) {
    int val = (int) (percentage * 100);
    int lpad = (int) (percentage * PBWIDTH);
    int rpad = PBWIDTH - lpad;
    printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, "");
    fflush(stdout);
}

Es wird ungefähr so ​​ausgegeben:

 75% [||||||||||||||||||||||||||||||||||||||||||               ]
Razz
quelle
Achtung, ';'am Ende des ersten sollte es keine geben #define!
Robb1
1
Dies ist die einfachste und beste Lösung, die ich bisher gefunden habe
Horro
Gibt es eine Möglichkeit, ein ASCII-Zeichen anstelle von | anzuzeigen? ? Danke
momo123
10

Sie können ein Wagenrücklaufzeichen ( \r) drucken , um den Ausgabe- "Cursor" zurück zum Anfang der aktuellen Zeile zu bewegen.

Schauen Sie sich für einen differenzierteren Ansatz etwas wie ncurses an (eine API für textbasierte Konsolenschnittstellen).

Matt Kline
quelle
4
+ manuelle Spülung, sonst wird es nicht sofort angezeigt, da die Ausgabe gepuffert ist.
Leemes
3
+ '\b'zum Bewegen des Cursors um eine Position nach links.
Alexey Frunze
1
+1 für Flüche. Auf jeden Fall der richtige Weg, wenn Sie etwas komplexeres tun möchten.
Leo
4

Ich weiß, dass ich diese Frage etwas spät beantworte, aber ich habe eine einfache Klasse erstellt, die genau das tut, was Sie wollen. (Denken Sie daran, dass ich using namespace std;vorher geschrieben habe.):

class pBar {
public:
    void update(double newProgress) {
        currentProgress += newProgress;
        amountOfFiller = (int)((currentProgress / neededProgress)*(double)pBarLength);
    }
    void print() {
        currUpdateVal %= pBarUpdater.length();
        cout << "\r" //Bring cursor to start of line
            << firstPartOfpBar; //Print out first part of pBar
        for (int a = 0; a < amountOfFiller; a++) { //Print out current progress
            cout << pBarFiller;
        }
        cout << pBarUpdater[currUpdateVal];
        for (int b = 0; b < pBarLength - amountOfFiller; b++) { //Print out spaces
            cout << " ";
        }
        cout << lastPartOfpBar //Print out last part of progress bar
            << " (" << (int)(100*(currentProgress/neededProgress)) << "%)" //This just prints out the percent
            << flush;
        currUpdateVal += 1;
    }
    std::string firstPartOfpBar = "[", //Change these at will (that is why I made them public)
        lastPartOfpBar = "]",
        pBarFiller = "|",
        pBarUpdater = "/-\\|";
private:
    int amountOfFiller,
        pBarLength = 50, //I would recommend NOT changing this
        currUpdateVal = 0; //Do not change
    double currentProgress = 0, //Do not change
        neededProgress = 100; //I would recommend NOT changing this
};

Ein Beispiel zur Verwendung:

int main() {
    //Setup:
    pBar bar;
    //Main loop:
    for (int i = 0; i < 100; i++) { //This can be any loop, but I just made this as an example
        //Update pBar:
        bar.update(1); //How much new progress was added (only needed when new progress was added)
        //Print pBar:
        bar.print(); //This should be called more frequently than it is in this demo (you'll have to see what looks best for your program)
        sleep(1);
    }
    cout << endl;
    return 0;
}

Hinweis: Ich habe alle Zeichenfolgen der Klassen veröffentlicht, damit das Erscheinungsbild der Leiste leicht geändert werden kann.

Flare Cat
quelle
3

Eine andere Möglichkeit könnte darin bestehen, die "Punkte" oder ein beliebiges Zeichen anzuzeigen. Der folgende Code gibt die Fortschrittsanzeige [Art des Ladens ...] alle 1 Sekunde als Punkte aus.

PS: Ich schlafe hier. Überlegen Sie zweimal, ob es um Leistung geht.

#include<iostream>
using namespace std;
int main()
{
    int count = 0;
    cout << "Will load in 10 Sec " << endl << "Loading ";
    for(count;count < 10; ++count){
        cout << ". " ;
        fflush(stdout);
        sleep(1);
    }
    cout << endl << "Done" <<endl;
    return 0;
}
AJ
quelle
1

Hier ist eine einfache, die ich gemacht habe:

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

using namespace std;

int barl = 20;

int main() {
   system("color 0e");  
   cout << "[";     
   for (int i = 0; i < barl; i++) {         
      Sleep(100);       
      cout << ":";  
   }
   cout << "]";
}
Kugel Blitz
quelle