コンソールアプリケーションのプログレスバー


82

ファイルをsftpサーバーにアップロードする簡単なc#コンソールアプリを書いています。ただし、ファイルの量は多いです。アップロードするファイルの総数から、アップロードしたファイルの割合、またはすでにアップロードしたファイルの数だけを表示したいと思います。

まず、すべてのファイルとファイルの総数を取得します。

string[] filePath = Directory.GetFiles(path, "*");
totalCount = filePath.Length;

次に、ファイルをループして、foreachループで1つずつアップロードします。

foreach(string file in filePath)
{
    string FileName = Path.GetFileName(file);
    //copy the files
    oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
    //Console.WriteLine("Uploading file..." + FileName);
    drawTextProgressBar(0, totalCount);
}

foreachループには、問題のあるプログレスバーがあります。正しく表示されません。

private static void drawTextProgressBar(int progress, int total)
{
    //draw empty progress bar
    Console.CursorLeft = 0;
    Console.Write("["); //start
    Console.CursorLeft = 32;
    Console.Write("]"); //end
    Console.CursorLeft = 1;
    float onechunk = 30.0f / total;

    //draw filled part
    int position = 1;
    for (int i = 0; i < onechunk * progress; i++)
    {
        Console.BackgroundColor = ConsoleColor.Gray;
        Console.CursorLeft = position++;
        Console.Write(" ");
    }

    //draw unfilled part
    for (int i = position; i <= 31 ; i++)
    {
        Console.BackgroundColor = ConsoleColor.Green;
        Console.CursorLeft = position++;
        Console.Write(" ");
    }

    //draw totals
    Console.CursorLeft = 35;
    Console.BackgroundColor = ConsoleColor.Black;
    Console.Write(progress.ToString() + " of " + total.ToString() + "    "); //blanks at the end remove any excess
}

出力は1943年のうちちょうど[] 0です

私はここで何が間違っているのですか?

編集:

XMLファイルのロードとエクスポート中にプログレスバーを表示しようとしています。ただし、ループを通過しています。最初のラウンドが終了すると、2番目のラウンドに進みます。

string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
foreach (string file in xmlFilePath)
{
     for (int i = 0; i < xmlFilePath.Length; i++)
     {
          //ExportXml(file, styleSheet);
          drawTextProgressBar(i, xmlCount);
          count++;
     }
 }

forループを離れることはありません...何か提案はありますか?


xmlCountとcountとは何ですか?
eddie_cat 2014

インクリメントするだけでカウントします。xmlCountは、指定されたフォルダー内のXMLファイルの総数です。DirectoryInfoxmlDir= new DirectoryInfo(xmlFullpath); xmlCount = xmlDir.GetFiles()。Length;
smr5 2014

1
また、foreachループ内にforループがあるのはなぜですか?同じことを繰り返しているようです。foreachループを維持する必要はおそらくありません。
eddie_cat 2014

1
外側のforeachループを削除しましたか?コメントビットを次のように変更しますExportXml(xmlFilePath[i])
eddie_cat 2014

1
それだけです。forループがあり、機能します。
smr5 2014

回答:


10

この行はあなたの問題です:

drawTextProgressBar(0, totalCount);

進行状況はすべての反復でゼロであると言っていますが、これは増分する必要があります。代わりにforループを使用するかもしれません。

for (int i = 0; i < filePath.length; i++)
{
    string FileName = Path.GetFileName(filePath[i]);
    //copy the files
    oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
    //Console.WriteLine("Uploading file..." + FileName);
    drawTextProgressBar(i, totalCount);
}

それは最初に機能しました、そして私は同じアップの他の場所で同じことをしています、そしてそれはループを引き起こしています。止まることはありません。投稿を更新しました。ご覧いただけますか?ありがとう。
smr5 2014

何を更新しましたか?それは私には同じように見えます、私は何が欠けていますか?
eddie_cat 2014

195

コンソールのプログレスバーも探していました。必要なことをするものが見つからなかったので、自分で転がすことにしました。ソースコード(MITライセンス)はこちらをクリックしてください

アニメーションの進行状況バー

特徴:

  • リダイレクトされた出力で動作します

    コンソールアプリケーション(例Program.exe > myfile.txt)の出力をリダイレクトすると、ほとんどの実装は例外を除いてクラッシュします。これは、リダイレクトされた出力をサポートConsole.CursorLeftしてConsole.SetCursorPosition()いないためです。

  • 実装 IProgress<double>

    これにより、[0..1]の範囲の進行状況を報告する非同期操作で進行状況バーを使用できます。

  • スレッドセーフ

  • 速い

    Consoleクラスは、その最悪のパフォーマンスで有名です。呼び出しが多すぎると、アプリケーションの速度が低下します。このクラスは、進行状況の更新を報告する頻度に関係なく、1秒あたり8回の呼び出しのみを実行します。

次のように使用します。

Console.Write("Performing some task... ");
using (var progress = new ProgressBar()) {
    for (int i = 0; i <= 100; i++) {
        progress.Report((double) i / 100);
        Thread.Sleep(20);
    }
}
Console.WriteLine("Done.");

3
これはかなりきれいに見えます!MITなどのOSSライセンスを追加することを検討しますか?choicealicense.com
ダニエルプレイスト2015

2
良いアイデア。それをしました。
ダニエルウルフ

@DanielWolf CursorPositionの変更からConsole.Writeをどのように取得しましたか?
JJS 2015年

1
@knocte:本番コードでは、確かにそうします。ここでの目標は、例をできるだけ簡潔に保ち、関連する部分の邪魔にならないようにすることでした。
ダニエルウルフ

8
gifは魅力的です。
レイヤン

16

これは古いスレッドであり、自己宣伝についてお詫びしますが、最近、nuget Goblinfactory で利用できるオープンソースのコンソールライブラリを作成しました。スレッドセーフな複数のプログレスバーをサポートするKonsoleは、このページを初めて使用する人がそれを必要とする場合に役立ちます。メインスレッドをブロックしません。

ダウンロードとタスクを並行して開始し、他のタスクを続行できるため、上記の回答とは多少異なります。

乾杯、これがお役に立てば幸いです

A

var t1 = Task.Run(()=> {
   var p = new ProgressBar("downloading music",10);
   ... do stuff
});

var t2 = Task.Run(()=> {
   var p = new ProgressBar("downloading video",10);
   ... do stuff
});

var t3 = Task.Run(()=> {
   var p = new ProgressBar("starting server",10);
   ... do stuff .. calling p.Refresh(n);
});

Task.WaitAll(new [] { t1,t2,t3 }, 20000);
Console.WriteLine("all done.");

このタイプの出力を提供します

ここに画像の説明を入力してください

nugetパッケージには、完全なクリッピングとラッピングをサポートするコンソールのウィンドウセクションに書き込むためのユーティリティに加えてPrintAt、その他のさまざまな役立つクラスも含まれています。

nugetパッケージを作成したのは、buildおよびopsコンソールスクリプトとユーティリティを作成するたびに、常に多くの一般的なコンソールルーチンを作成することになったためです。

複数のファイルをダウンロードする場合、私はConsole.Write各スレッドの画面にゆっくりと移動し、画面上のインターリーブされた出力を読みやすくするためにさまざまなトリックを試していました。たとえば、色や数字が異なります。最終的には、さまざまなスレッドからの出力をさまざまなウィンドウに簡単に印刷できるようにウィンドウライブラリを作成しました。これにより、ユーティリティスクリプトの定型コードが大幅に削減されました。

たとえば、このコードは、

        var con = new Window(200,50);
        con.WriteLine("starting client server demo");
        var client = new Window(1, 4, 20, 20, ConsoleColor.Gray, ConsoleColor.DarkBlue, con);
        var server = new Window(25, 4, 20, 20, con);
        client.WriteLine("CLIENT");
        client.WriteLine("------");
        server.WriteLine("SERVER");
        server.WriteLine("------");
        client.WriteLine("<-- PUT some long text to show wrapping");
        server.WriteLine(ConsoleColor.DarkYellow, "--> PUT some long text to show wrapping");
        server.WriteLine(ConsoleColor.Red, "<-- 404|Not Found|some long text to show wrapping|");
        client.WriteLine(ConsoleColor.Red, "--> 404|Not Found|some long text to show wrapping|");

        con.WriteLine("starting names demo");
        // let's open a window with a box around it by using Window.Open
        var names = Window.Open(50, 4, 40, 10, "names");
        TestData.MakeNames(40).OrderByDescending(n => n).ToList()
             .ForEach(n => names.WriteLine(n));

        con.WriteLine("starting numbers demo");
        var numbers = Window.Open(50, 15, 40, 10, "numbers", 
              LineThickNess.Double,ConsoleColor.White,ConsoleColor.Blue);
        Enumerable.Range(1,200).ToList()
             .ForEach(i => numbers.WriteLine(i.ToString())); // shows scrolling

これを生成します

ここに画像の説明を入力してください

ウィンドウに書き込むのと同じくらい簡単に、ウィンドウ内にプログレスバーを作成することもできます。(ミックスアンドマッチ)。


これは最高です
Pratik 2018

9

https://www.nuget.org/packages/ShellProgressBar/を試してみることをお勧めします

私はちょうどこのプログレスバーの実装に出くわしました-そのクロスプラットフォーム、本当に使いやすく、非常に構成可能で、箱から出してすぐにすべきことを実行します。

とても気に入ったので共有するだけです。


6

ProgressBarメソッドをコピーして貼り付けました。あなたのエラーは、受け入れられた答えが述べたようにループにあったからです。ただし、このProgressBarメソッドにはいくつかの構文エラーもあります。これが作業バージョンです。少し変更。

private static void ProgressBar(int progress, int tot)
{
    //draw empty progress bar
    Console.CursorLeft = 0;
    Console.Write("["); //start
    Console.CursorLeft = 32;
    Console.Write("]"); //end
    Console.CursorLeft = 1;
    float onechunk = 30.0f / tot;

    //draw filled part
    int position = 1;
    for (int i = 0; i < onechunk * progress; i++)
    {
        Console.BackgroundColor = ConsoleColor.Green;
        Console.CursorLeft = position++;
        Console.Write(" ");
    }

    //draw unfilled part
    for (int i = position; i <= 31; i++)
    {
        Console.BackgroundColor = ConsoleColor.Gray;
        Console.CursorLeft = position++;
        Console.Write(" ");
    }

    //draw totals
    Console.CursorLeft = 35;
    Console.BackgroundColor = ConsoleColor.Black;
    Console.Write(progress.ToString() + " of " + tot.ToString() + "    "); //blanks at the end remove any excess
}

@ Daniel-wolfの方が優れたアプローチであることに注意してください:https//stackoverflow.com/a/31193455/169714


5

元のポスターのプログレスバーはとても気に入りましたが、特定の進行状況/アイテムの合計の組み合わせで進行状況が正しく表示されないことがわかりました。たとえば、次の例では正しく描画されず、進行状況バーの最後に余分な灰色のブロックが残ります。

drawTextProgressBar(4114, 4114)

上記の問題を修正し、物事をかなりスピードアップした不要なループを削除するために、いくつかの描画コードをやり直しました。

public static void drawTextProgressBar(string stepDescription, int progress, int total)
{
    int totalChunks = 30;

    //draw empty progress bar
    Console.CursorLeft = 0;
    Console.Write("["); //start
    Console.CursorLeft = totalChunks + 1;
    Console.Write("]"); //end
    Console.CursorLeft = 1;

    double pctComplete = Convert.ToDouble(progress) / total;
    int numChunksComplete = Convert.ToInt16(totalChunks * pctComplete);

    //draw completed chunks
    Console.BackgroundColor = ConsoleColor.Green;
    Console.Write("".PadRight(numChunksComplete));

    //draw incomplete chunks
    Console.BackgroundColor = ConsoleColor.Gray;
    Console.Write("".PadRight(totalChunks - numChunksComplete));

    //draw totals
    Console.CursorLeft = totalChunks + 5;
    Console.BackgroundColor = ConsoleColor.Black;

    string output = progress.ToString() + " of " + total.ToString();
    Console.Write(output.PadRight(15) + stepDescription); //pad the output so when changing from 3 to 4 digits we avoid text shifting
}

それは、それは後に新しい行に移動しません。その前に、任意のテキストのような以前のコンソール出力を削除することを除いて、一般的にこの作品....
デヴィッドShnayder

5

System.Reactiveで動作するこの便利なクラスを作成しました。十分に素敵だと思います。

public class ConsoleDisplayUpdater : IDisposable
{
    private readonly IDisposable progressUpdater;

    public ConsoleDisplayUpdater(IObservable<double> progress)
    {
        progressUpdater = progress.Subscribe(DisplayProgress);
    }

    public int Width { get; set; } = 50;

    private void DisplayProgress(double progress)
    {
        if (double.IsNaN(progress))
        {
            return;
        }

        var progressBarLenght = progress * Width;
        System.Console.CursorLeft = 0;
        System.Console.Write("[");
        var bar = new string(Enumerable.Range(1, (int) progressBarLenght).Select(_ => '=').ToArray());

        System.Console.Write(bar);

        var label = $@"{progress:P0}";
        System.Console.CursorLeft = (Width -label.Length) / 2;
        System.Console.Write(label);
        System.Console.CursorLeft = Width;
        System.Console.Write("]");
    }

    public void Dispose()
    {
        progressUpdater?.Dispose();
    }
}

0

このスレッドで何か他のものを探しているのに出くわし、DownloadProgressChangedを使用してファイルのリストをダウンロードするためにまとめたコードを削除すると思いました。これは非常に役立つので、進行状況だけでなく、ファイルが通過するときの実際のサイズも確認できます。それが誰かを助けることを願っています!

public static bool DownloadFile(List<string> files, string host, string username, string password, string savePath)
    {
        try
        {
            //setup FTP client

            foreach (string f in files)
            {
                FILENAME = f.Split('\\').Last();
                wc.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
                wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
                wc.DownloadFileAsync(new Uri(host + f), savePath + f);
                while (wc.IsBusy)
                    System.Threading.Thread.Sleep(1000);
                Console.Write("  COMPLETED!");
                Console.WriteLine();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            return false;
        }
        return true;
    }

    private static void ProgressChanged(object obj, System.Net.DownloadProgressChangedEventArgs e)
    {
        Console.Write("\r --> Downloading " + FILENAME +": " + string.Format("{0:n0}", e.BytesReceived / 1000) + " kb");
    }

    private static void Completed(object obj, AsyncCompletedEventArgs e)
    {
    }

出力の例を次に示します。 ここに画像の説明を入力してください

それが誰かを助けることを願っています!


2
@regisbsbこれらはプログレスバーではありません。彼はファイル名の一部を検閲したようです:)私も最初はだまされていました。
シルクファイア2016年

-1

私はまだ少し新しいですC#が、以下が役立つかもしれないと思います。

string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
int count = 0;
foreach (string file in xmlFilePath)
{
    //ExportXml(file, styleSheet);
    drawTextProgressBar(count, xmlCount);
    count++;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.