控制台应用程序的进度条

时间:2014-07-23 19:01:48

标签: c# console progress

我正在编写一个简单的c#控制台应用程序,它将文件上传到sftp服务器。但是,文件量很大。我想显示已上传文件的百分比或仅显示已上传文件的数量。

首先,我获取所有文件和文件总数。

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

然后我遍历文件并在foreach循环中逐个上传。

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文件时显示进度条。然而,它正在经历一个循环。完成第一轮后,它会进入第二轮,依此类推。

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循环......有什么建议吗?

10 个答案:

答案 0 :(得分:146)

我还在寻找一个控制台进度条。我没有找到一个能做我需要的东西,所以我决定自己动手。 Click here for the source code(麻省理工学院执照)。

Animated progress bar

特点:

  • 使用重定向输出

    如果重定向控制台应用程序的输出(例如Program.exe > myfile.txt),大多数实现都会因异常而崩溃。这是因为Console.CursorLeftConsole.SetCursorPosition()不支持重定向输出。

  • 实施IProgress<double>

    这允许您使用进度条和异步操作来报告[0..1]范围内的进度。

  • <强>线程安全

  • <强>快速

    Console班因其糟糕的表现而臭名昭着。对它的调用太多,您的应用程序运行速度变慢。无论您报告进度更新的频率如何,此类每秒仅执行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.");

答案 1 :(得分:11)

我知道这是一个老线程,并为自我推销道歉,但我最近在nuget moment-duration-format上编写了一个开源控制台库,其中包含线程安全的多个进度条支持,这可能对任何人都有帮助需要一个不阻止主线程的页面的新手。

它与上述答案略有不同,因为它允许您并行启动下载和任务并继续执行其他任务;

欢呼,希望这是有帮助的

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

为您提供此类输出

Goblinfactory.Konsole

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

产生这个

enter image description here

您也可以在窗口内创建进度条,就像写入窗口一样容易。 (混合搭配)。

答案 2 :(得分:9)

这一行是你的问题:

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

答案 3 :(得分:5)

我已复制粘贴您的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

答案 4 :(得分:3)

我非常喜欢原版海报的进度条,但发现它没有正确显示某些进度/总项目组合的进度。例如,以下内容未正确绘制,在进度条的末尾留下了一个额外的灰色块:

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
}

答案 5 :(得分:2)

您可能想尝试https://www.nuget.org/packages/ShellProgressBar/

我只是偶然发现了这个进度条实现 - 它的跨平台,真的易于使用,可配置完成,并且开箱即用。

分享,因为我很喜欢它。

答案 6 :(得分:1)

我创建了这个与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();
    }
}

https://github.com/WOA-project/WOA-Deployer/blob/master/Source/Deployer.Lumia.Console/ConsoleDisplayUpdater.cs

答案 7 :(得分: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)
    {
    }

这是输出的一个例子: enter image description here

希望它有所帮助!

答案 8 :(得分:0)

基于以上所有帖子,我做了一个改进版。

  1. 没有跳跃光标。现在看不见了。

  2. 提高了性能(成本是原始版本的 1/5~1/10 倍)。

  3. 基于接口。很容易转移到别的地方。

     public class ConsoleProgressBar : IProgressBar
     {
         private const ConsoleColor ForeColor = ConsoleColor.Green;
         private const ConsoleColor BkColor = ConsoleColor.Gray;
         private const int DefaultWidthOfBar = 32;
         private const int TextMarginLeft = 3;
    
         private readonly int _total;
         private readonly int _widthOfBar;
    
         public ConsoleProgressBar(int total, int widthOfBar = DefaultWidthOfBar)
         {
             _total = total;
             _widthOfBar = widthOfBar;
         }
    
         private bool _intited;
         public void Init()
         {
             _lastPosition = 0;
    
             //Draw empty progress bar
             Console.CursorVisible = false;
             Console.CursorLeft = 0;
             Console.Write("["); //start
             Console.CursorLeft = _widthOfBar;
             Console.Write("]"); //end
             Console.CursorLeft = 1;
    
             //Draw background bar
             for (var position = 1; position < _widthOfBar; position++) //Skip the first position which is "[".
             {
                 Console.BackgroundColor = BkColor;
                 Console.CursorLeft = position;
                 Console.Write(" ");
             }
         }
    
         public void ShowProgress(int currentCount)
         {
             if (!_intited)
             {
                 Init();
                 _intited = true;
             }
             DrawTextProgressBar(currentCount);
         }
    
         private int _lastPosition;
    
         public void DrawTextProgressBar(int currentCount)
         {
             //Draw current chunk.
             var position = currentCount * _widthOfBar / _total;
             if (position != _lastPosition)
             {
                 _lastPosition = position;
                 Console.BackgroundColor = ForeColor;
                 Console.CursorLeft = position >= _widthOfBar ? _widthOfBar - 1 : position;
                 Console.Write(" ");
             }
    
             //Draw totals
             Console.CursorLeft = _widthOfBar + TextMarginLeft;
             Console.BackgroundColor = ConsoleColor.Black;
             Console.Write(currentCount + " of " + _total + "    "); //blanks at the end remove any excess
         }
     }
    
     public interface IProgressBar
     {
         public void ShowProgress(int currentCount);
     }
    

还有一些测试代码:

        var total = 100;
        IProgressBar progressBar = new ConsoleProgressBar(total);
        for (var i = 0; i <= total; i++)
        {
            progressBar.ShowProgress(i);
            Thread.Sleep(50);
        }

        Thread.Sleep(500);
        Console.Clear();

        total = 9999;
        progressBar = new ConsoleProgressBar(total);
        for (var i = 0; i <= total; i++)
        {
            progressBar.ShowProgress(i);
        }

答案 9 :(得分:-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++;
}