콘솔 애플리케이션의 진행률 표시 줄


83

sftp 서버에 파일을 업로드하는 간단한 C # 콘솔 앱을 작성 중입니다. 그러나 파일의 양이 많습니다. 업로드 할 총 파일 수에서 업로드 된 파일의 비율 또는 이미 업로드 된 파일 수를 표시하고 싶습니다.

먼저 모든 파일과 총 파일 수를 얻습니다.

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 루프를 떠나지 않습니다 ... 제안 사항이 있습니까?


xmlCount와 count는 무엇입니까?
eddie_cat

단지 증가를 계산하십시오. xmlCount는 지정된 폴더에있는 총 XML 파일 수입니다. DirectoryInfo xmlDir = new DirectoryInfo (xmlFullpath); xmlCount = xmlDir.GetFiles (). Length;
smr5

1
또한 for 루프가 foreach 루프 안에있는 이유는 무엇입니까? 같은 일을 반복하는 것 같습니다. foreach 루프를 유지할 필요는 없습니다.
eddie_cat 2014

1
바깥 쪽 foreach 루프를 제거 했습니까? 변화에 대한 주석 비트ExportXml(xmlFilePath[i])
eddie_cat

1
그거였다. for 루프가 있으며 작동합니다.
smr5

답변:


11

이 줄이 문제입니다.

drawTextProgressBar(0, totalCount);

당신은 모든 반복에서 진행률이 0이라고 말하고 있습니다. 이것은 증가되어야합니다. 대신 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

196

콘솔 진행률 표시 줄도 찾고있었습니다. 나는 내가 필요로하는 일을하는 것을 찾지 못해서 나만의 것을 결정하기로 결정했습니다. 소스 코드 (MIT 라이선스) 를 보려면 여기를 클릭하십시오 .

애니메이션 진행률 표시 줄

풍모:

  • 리디렉션 된 출력과 함께 작동

    콘솔 애플리케이션 (예 :)의 출력을 리디렉션하면 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.");

3
이것은 꽤 깔끔해 보입니다! 여기에 MIT와 같은 OSS 라이센스를 추가하는 것을 고려 하시겠습니까? choosealicense.com
다니엘 Plaisted

2
좋은 생각. 했어요.
Daniel Wolf

@DanielWolf CursorPosition을 변경하여 Console.Write를 어떻게 얻었습니까?
JJS

1
@knocte : 프로덕션 코드에서는 확실히 그렇습니다. 여기서 목표는 가능한 한 간결하게 예제를 유지하고 관련 부분에서주의를 분산시키지 않는 것입니다.
다니엘 늑대

8
gif가 매력적입니다.
Lei Yang

16

나는 이것이 오래된 스레드라는 것을 알고 있으며 자체 프로모션에 대해 사과하지만 최근에 nuget Goblinfactory 에서 사용할 수있는 오픈 소스 콘솔 라이브러리를 작성 했습니다. 메인 스레드를 차단하지 않습니다.

다운로드 및 작업을 병렬로 시작하고 다른 작업을 계속할 수 있으므로 위의 답변과 다소 다릅니다.

건배, 도움이 되었기를 바랍니다.

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 패키지를 작성했습니다.

여러 파일을 다운로드하는 경우 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

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
}

이것은 이전의 모든 텍스트와 같은 이전 콘솔 출력을 삭제하고 이후 새 줄로 이동하지 않는다는 점을 제외하고는 일반적으로 작동합니다 ....
David 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 진행률 표시 줄이 아닙니다. 파일 이름의 일부를 검열 한 것 같습니다. :) 저도 처음에는 속았습니다.
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++;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.