web-dev-qa-db-ja.com

コンソールアプリケーションの進行状況バー

ファイルを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ループが終了することはありません...提案はありますか?

66
smr5

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

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);
}
9
eddie_cat

コンソールの進行状況バーも探していました。必要なことをするものが見つからなかったので、自分でロールバックすることにしました。 ソースコードはここをクリック (MITライセンス)。

Animated progress bar

特徴:

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

    コンソールアプリケーションの出力(たとえば、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.");
153
Daniel Wolf

これは古いスレッドであり、セルフプロモーションをおologiesびしますが、最近、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.");

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

enter image description here

Nugetパッケージには、完全なクリッピングとラッピングのサポート、およびPrintAtおよびその他のさまざまな有用なクラスを使用して、コンソールのウィンドウセクションに書き込むためのユーティリティも含まれています。

Nugetパッケージを作成したのは、ビルドおよび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

これを作り出す

enter image description here

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

12
snowcode

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

6
JP Hellemons

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

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
}
4
Nico M

試してみてください https://www.nuget.org/packages/ShellProgressBar/

私はこのプログレスバーの実装を偶然見つけました-そのクロスプラットフォーム、本当に使いやすく、非常に設定可能で、箱から出してすぐにすべきことをします。

私はそれがとても好きだったので、ただ共有しています。

4
Schweder

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();
    }
}
2
SuperJMN

私はこのスレッドにつまずいて何か他のものを探していましたが、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)
    {
    }

出力の例を次に示します。 enter image description here

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

0
Ted Krapf