Индикатор выполнения в консольном приложении
Я пишу просто консольное приложение С#, которое загружает файлы на сервер 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
}
Вывод только [] 0 из 1943
Что я здесь делаю неправильно?
EDIT:
Я пытаюсь отобразить индикатор выполнения, пока я загружаю и экспортирую файлы 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... Любые предложения?
Ответы
Ответ 1
Эта строка является вашей проблемой:
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);
}
Ответ 2
Я также искал панель прогресса консоли. Я не нашел того, что делал то, что мне было нужно, поэтому я решил бросить свое. Щелкните здесь для получения исходного кода (лицензия MIT).
![Animated progress bar]()
Особенности:
-
Работает с перенаправленным выходом
Если вы перенаправляете вывод консольного приложения (например, 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.");
Ответ 3
Я знаю, что это старая ветка, и я извиняюсь за саморекламу, однако недавно я написал консольную библиотеку с открытым исходным кодом, доступную на nuget Goblinfactory.Konsole с поддержкой многопотоковой поддержки потока, которая может помочь любому новичку на этой странице, нуждающемуся в том, что не блокирует основной поток.
Это несколько отличается от ответов выше, так как позволяет начинать загрузки и задачи параллельно и продолжать выполнение других задач;
ура, надеюсь, это полезно
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, потому что постоянно писал множество общих подпрограмм для консоли всякий раз, когда писал сценарии и утилиты для консоли 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 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]()
Вы также можете создавать индикаторы выполнения в окне так же легко, как писать в окна. (смешивать и сочетать).
Ответ 4
У меня есть копия, наклеенная вашим методом 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 есть лучший подход: fooobar.com/questions/122415/...
Ответ 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
}
Ответ 6
Вы можете попробовать https://www.nuget.org/packages/ShellProgressBar/
Я только что наткнулся на эту реализацию индикатора выполнения - ее кроссплатформенность, действительно проста в использовании, вполне настраиваема и делает то, что она должна прямо из коробки.
Просто делюсь, потому что мне это очень понравилось.
Ответ 7
Я создал этот удобный класс, который работает с 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 = [email protected]"{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();
}
}
Ответ 8
Я просто наткнулся на эту тему, ища что-то еще, и мне показалось, что я скрою свой код, который я собрал, который загружает список файлов с помощью 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]()
Надеюсь, это поможет кому-то!
Ответ 9
Я все еще немного новичок в 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++;
}