Індикатор виконання в консольному додатку


84

Я пишу просту консольну програму c #, яка завантажує файли на сервер sftp. Однак кількість файлів велика. Я хотів би відобразити або відсоток завантажених файлів, або лише кількість завантажених файлів із загальної кількості завантажених файлів.

По-перше, я отримую всі файли та загальну кількість файлів.

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

Потім я прокручую файл і завантажую їх по одному в кожен цикл.

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
}

Результат - лише [] 0 з 1943 року

Що я тут роблю не так?

РЕДАГУВАТИ:

Я намагаюся відобразити індикатор виконання під час завантаження та експорту 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 ... Будь-які пропозиції?


Що таке xmlCount і count?
eddie_cat

Порахувати лише збільшення. xmlCount - це лише загальна кількість XML-файлів у зазначеній папці DirectoryInfo xmlDir = new DirectoryInfo (xmlFullpath); xmlCount = xmlDir.GetFiles (). Довжина;
smr5

1
Крім того, чому цикл for знаходиться всередині цикла foreach? Здається, повторюється те саме. Ймовірно, не потрібно тримати цикл foreach.
eddie_cat

1
Ви видалили зовнішню петлю foreach? Зміна коментованого біта наExportXml(xmlFilePath[i])
eddie_cat

1
Це було все. У мене просто є цикл for, і він працює.
smr5

Відповіді:


11

Цей рядок - ваша проблема:

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

Що ви оновили? Мені здається однаково, чого мені не вистачає?
eddie_cat

200

Я також шукав консоль індикатора прогресу. Я не знайшов жодного, який би зробив те, що мені потрібно, тому я вирішив скотити своє. Клацніть тут для вихідного коду (ліцензія MIT).

Анімована панель прогресу

Особливості:

  • Працює з переспрямованим результатом

    Якщо ви перенаправляєте висновок консольного додатка (наприклад, Program.exe > myfile.txt), більшість реалізацій аварійно завершать роботу за винятком. Це тому, що Console.CursorLeftі Console.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.");

4
Це виглядає досить акуратно! Чи не могли б Ви додати до нього ліцензію OSS, таку як MIT? choosealicense.com
Деніел Пліс

2
Гарна ідея. Зроблено.
Даніель Вольф,

@DanielWolf, як ти отримав Console.Write від зміни курсору?
JJS

1
@knocte: У виробничому коді я б точно. Метою тут було зберегти приклад якомога коротше і не відволікати увагу від відповідних частин.
Даніель Вольф

8
GIF привабливий.
Lei Yang

18

Я знаю, що це старий потік, і вибачення за саморекламу, однак нещодавно я написав консольну бібліотеку з відкритим кодом, доступну на 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

виробляє це

введіть тут опис зображення

Ви також можете створювати індикатори прогресу всередині вікна так само просто, як писати у вікна. (змішувати та поєднувати).


Це найкраще
Пратік,

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


6

Я створив цей зручний клас, який працює з 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();
    }
}

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
}

це працює загалом, за винятком того, що він видаляє попередні результати консолі, як і будь-який текст до цього, і що він не переходить до нового рядка після ....
Девід Шнайдер,

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 Це не смуги прогресу, схоже, він цензурував частину назв файлів :) Я знаю, спочатку мене також обдурили.
silkfire

-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++;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.