C # Windows 콘솔 앱에서 현재 줄을 어떻게 업데이트합니까?


507

C #에서 Windows 콘솔 앱을 빌드 할 때 현재 줄을 연장하거나 새 줄로 갈 필요없이 콘솔에 쓸 수 있습니까? 예를 들어, 프로세스가 완료되는 정도를 나타내는 백분율을 표시하려면 커서와 동일한 행의 값을 업데이트하고 각 백분율을 새 행에 넣지 않아도됩니다.

"표준"C # 콘솔 앱으로이 작업을 수행 할 수 있습니까?


멋진 명령 줄 인터페이스에 관심이 있다면 curses / ncurses를 확인해야합니다.
Charles Addis

@CharlesAddis이지만 curses / ncurses는 C ++에서만 작동합니까?
Xam

답변:


783

"\r"콘솔 에만 인쇄 하면 커서가 현재 줄의 시작 부분으로 돌아가서 다시 쓸 수 있습니다. 트릭을 수행해야합니다.

for(int i = 0; i < 100; ++i)
{
    Console.Write("\r{0}%   ", i);
}

숫자 뒤에 약간의 공백이있어 이전에 있던 것이 지워 졌는지 확인하십시오.
또한 줄 끝에 "\ n"을 추가하고 싶지 않기 때문에 Write()대신 사용 WriteLine()하십시오.


7
for (int i = 0; i <= 100; ++ i)는 100 %로갑니다
Nicolas Tyler

13
이전 쓰기가 새 쓰기보다 길었을 때 어떻게 처리합니까? 콘솔의 너비를 가져 와서 줄을 공백으로 채우는 방법이 있습니까?
Drew Chapin

6
@druciferre 내 머리 꼭대기에서 나는 당신의 질문에 대한 두 가지 대답을 생각할 수 있습니다. 둘 다 현재 출력을 문자열로 먼저 저장하고 다음과 같이 설정된 양의 문자로 채 웁니다. Console.Write ( "\ r {0}", strOutput.PadRight (nPaddingCount, '')); "nPaddingCount"는 직접 설정 한 숫자이거나 이전 출력을 추적하고 이전과 현재 출력과 현재 출력 길이의 길이 차이로 nPaddingCount를 설정할 수 있습니다. nPaddingCount가 음수이면 abs (prev.len-curr.len)을 수행하지 않으면 PadRight를 사용할 필요가 없습니다.
John Odom

1
@malgm 잘 구성된 코드. 12 개의 스레드 중 하나라도 원하는 때에 콘솔에 쓸 수 있다면 새로운 줄을 작성하든 아니든 관계없이 문제가 발생합니다.
Mark

2
@JohnOdom 당신은 이전 (패딩되지 않은) 출력 길이를 유지 한 다음 첫 번째 인수로 입력해야합니다 PadRight(패딩되지 않은 문자열 또는 길이를 먼저 저장하십시오).
Jesper Matthiesen

254

Console.SetCursorPosition커서의 위치를 ​​설정 한 다음 현재 위치에 쓸 수 있습니다 .

다음은 간단한 "스피너" 보여주는 예입니다 .

static void Main(string[] args)
{
    var spin = new ConsoleSpinner();
    Console.Write("Working....");
    while (true) 
    {
        spin.Turn();
    }
}

public class ConsoleSpinner
{
    int counter;

    public void Turn()
    {
        counter++;        
        switch (counter % 4)
        {
            case 0: Console.Write("/"); counter = 0; break;
            case 1: Console.Write("-"); break;
            case 2: Console.Write("\\"); break;
            case 3: Console.Write("|"); break;
        }
        Thread.Sleep(100);
        Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
    }
}

기존 출력을 새 출력 또는 공백으로 덮어 써야합니다.

업데이트 :이 예제는 커서를 한 문자 만 뒤로 이동시키는 것으로 비판되었으므로 설명을 위해 추가합니다 SetCursorPosition. 콘솔 창에서 커서를 원하는 위치로 설정할 수 있습니다.

Console.SetCursorPosition(0, Console.CursorTop);

커서를 현재 줄의 시작 부분으로 설정하거나 Console.CursorLeft = 0직접 사용할 수 있습니다 .


8
문제는 \ r을 사용하여 해결할 수 있지만 SetCursorPosition(또는 CursorLeft)를 사용하면 줄의 시작 부분에 쓰지 않고 창에서 위로 움직이는 등의 유연성이 향상되므로 출력과 같은 일반적인 접근 방식입니다. 사용자 정의 진행률 표시 줄 또는 ASCII 그래픽.
Dirk Vollmar

14
장황하고 의무의 부름을 넘어서서 +1합니다. 좋은 물건 감사합니다.
Copas

1
다른 방법으로 보여 +1 다른 사람들은 모두 \ r을 보여 주었고 OP가 단순히 백분율을 업데이트하는 경우 전체 줄을 다시 쓰지 않고도 값을 업데이트 할 수 있습니다. OP는 실제로 줄의 시작 부분으로 이동하고 싶다고 말한 적이 없으며 커서와 같은 줄에서 무언가를 업데이트하고 싶었습니다.
Andy

1
SetCursorPosition의 추가 유연성은 약간의 속도를 희생하고 루프가 사용자가 알아 채기에 충분히 길면 눈에 띄는 커서 깜박임이 발생합니다. 아래의 테스트 의견을 참조하십시오.
Kevin

5
또한 줄 길이로 인해 콘솔이 다음 줄로 줄지 않는지 확인하십시오. 그렇지 않으면 콘솔 창에서 실행중인 내용에 문제가있을 수 있습니다.
Mandrake

84

지금까지이 작업을 수행하는 방법에 대한 세 가지 경쟁 대안이 있습니다.

Console.Write("\r{0}   ", value);                      // Option 1: carriage return
Console.Write("\b\b\b\b\b{0}", value);                 // Option 2: backspace
{                                                      // Option 3 in two parts:
    Console.SetCursorPosition(0, Console.CursorTop);   // - Move cursor
    Console.Write(value);                              // - Rewrite
}

나는 항상 Console.CursorLeft = 0세 번째 옵션의 변형 인을 사용 했으므로 테스트를하기로 결정했습니다. 내가 사용한 코드는 다음과 같습니다.

public static void CursorTest()
{
    int testsize = 1000000;

    Console.WriteLine("Testing cursor position");
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < testsize; i++)
    {
        Console.Write("\rCounting: {0}     ", i);
    }
    sw.Stop();
    Console.WriteLine("\nTime using \\r: {0}", sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    int top = Console.CursorTop;
    for (int i = 0; i < testsize; i++)
    {
        Console.SetCursorPosition(0, top);        
        Console.Write("Counting: {0}     ", i);
    }
    sw.Stop();
    Console.WriteLine("\nTime using CursorLeft: {0}", sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    Console.Write("Counting:          ");
    for (int i = 0; i < testsize; i++)
    {        
        Console.Write("\b\b\b\b\b\b\b\b{0,8}", i);
    }

    sw.Stop();
    Console.WriteLine("\nTime using \\b: {0}", sw.ElapsedMilliseconds);
}

내 컴퓨터에서 다음과 같은 결과가 나타납니다.

  • 백 스페이스 : 25.0 초
  • 캐리지 리턴 : 28.7 초
  • SetCursorPosition : 49.7 초

또한 SetCursorPosition대안 중 하나에서 관찰하지 않은 눈에 띄는 깜박임이 발생했습니다. 그래서, 도덕적이다 사용 백 스페이스 또는 캐리지 리턴이 가능 하고, 나에게 가르쳐 주셔서 감사 SO,이 작업을 수행하는 빠른 방법!


업데이트 : 의견에서 Joel은 SetCursorPosition은 이동 거리에 대해 일정하지만 다른 방법은 선형이라고 제안합니다. 추가 테스트에서이 경우에 해당하는지 확인 하지만, 일정한 시간과 느린 속도는 여전히 느립니다. 필자의 테스트에서 긴 백 스페이스 문자열을 콘솔에 쓰는 것이 60 자 정도가 될 때까지 SetCursorPosition보다 빠릅니다. 백 스페이스 빠르게 라인의보다 짧은 60 자 (정도) 부분을 교체하고, 그래서 그리고 그것은 내가 \ 연구를 통해 \ B의 내 최초의 승인에 의해 서가는거야, 그래서 깜박임을하지 않습니다 SetCursorPosition.


4
해당 작업의 효율성은 실제로 중요하지 않습니다. 사용자가 알기에는 너무 빠릅니다. 불필요한 미세 최적화는 나쁘다.
Malfist

@Malfist : 루프의 길이에 따라 사용자는 눈치 채지 못할 수도 있습니다. 위의 편집에서 (댓글을보기 전에) 추가 한 것처럼 SetCursorPosition은 깜박임을 도입했으며 다른 옵션보다 거의 두 배 오래 걸립니다.
Kevin

1
나는 그것이 마이크로 최적화 (백만 번 실행하고 50 초가 걸리는 것은 여전히 ​​매우 적은 시간)이며 결과에 대해 +1이며 확실히 아는 것이 매우 유용하다는 데 동의합니다.
Andy

6
벤치 마크에는 근본적인 결함이 있습니다. SetCursorPosition () 시간은 커서의 이동 거리에 관계없이 동일하지만 다른 옵션은 콘솔이 처리해야하는 문자 수에 따라 다릅니다.
Joel Coehoorn

1
이것은 사용 가능한 다양한 옵션을 요약 한 것입니다. 그러나 \ r을 사용할 때도 깜박입니다. \ b를 사용하면 수정 텍스트 ( "Counting :")가 다시 작성되지 않으므로 깜박임이 없습니다. \ b를 추가하고 \ b 및 SetCursorPosition에서 발생하는 수정 텍스트를 다시 쓰면 깜박임이 발생합니다. Joel의 말 : Joel은 기본적으로 옳지 만 \ r은 여전히 ​​매우 긴 줄에서 SetCursorPosition보다 성능이 뛰어나지 만 차이는 줄어 듭니다.
Dirk Vollmar

27

당신은 사용할 수 있습니다 \ B 현재 행에 백업 문자의 특정 번호를 (백 스페이스) 이스케이프 시퀀스를. 이것은 현재 위치 만 이동 시키며 문자를 제거하지는 않습니다.

예를 들면 다음과 같습니다.

string line="";

for(int i=0; i<100; i++)
{
    string backup=new string('\b',line.Length);
    Console.Write(backup);
    line=string.Format("{0}%",i);
    Console.Write(line);
}

여기서 line 은 콘솔에 쓸 백분율 줄입니다. 트릭은 이전 출력에 대해 올바른 수의 \ b 문자 를 생성하는 것 입니다.

\ r 접근 방식에 비해이 방법 의 장점은 백분율 출력이 줄의 시작 부분에없는 경우에도 작동한다는 것입니다.


1
+1, 이것은 제시된 가장 빠른 방법으로 판명되었습니다 (아래의 테스트 주석 참조)
Kevin

19

\r이러한 시나리오에 사용됩니다.
\r 는 캐리지 리턴을 나타냅니다. 즉 커서가 줄의 시작으로 돌아갑니다.
이것이 Windows가 \n\r새로운 라인 마커로 사용 하는 이유 입니다.
\n줄 아래로 이동하고 줄 \r의 시작 부분으로 돌아갑니다.


22
실제로는 \ r \ n입니다.
Joel Mueller

14

나는 단지 divo의 ConsoleSpinner수업 과 놀아야했다 . 내 간결한 곳은 어디에도 없지만 그 클래스의 사용자가 자신의 while(true)루프 를 작성해야한다는 것은 나와 함께 잘 앉아 있지 않았습니다 . 나는 다음과 같은 경험을 위해 촬영 중이다.

static void Main(string[] args)
{
    Console.Write("Working....");
    ConsoleSpinner spin = new ConsoleSpinner();
    spin.Start();

    // Do some work...

    spin.Stop(); 
}

그리고 나는 아래 코드로 그것을 깨달았습니다. 내 Start()메소드가 차단되는 것을 원하지 않기 때문에 사용자가 while(spinFlag)유사 루프를 작성하는 것에 대해 걱정할 필요가 없으며 여러 스피너를 동시에 처리하기 위해 별도의 스레드를 생성해야합니다. 제사. 그리고 이것은 코드가 훨씬 더 복잡해야 함을 의미합니다.

또한, 나는 그렇게 많은 멀티 스레딩을하지 않았으므로 미묘한 버그 또는 세 가지를 남겼을 가능성이 있습니다 (아마도). 그러나 지금까지는 잘 작동하는 것 같습니다.

public class ConsoleSpinner : IDisposable
{       
    public ConsoleSpinner()
    {
        CursorLeft = Console.CursorLeft;
        CursorTop = Console.CursorTop;  
    }

    public ConsoleSpinner(bool start)
        : this()
    {
        if (start) Start();
    }

    public void Start()
    {
        // prevent two conflicting Start() calls ot the same instance
        lock (instanceLocker) 
        {
            if (!running )
            {
                running = true;
                turner = new Thread(Turn);
                turner.Start();
            }
        }
    }

    public void StartHere()
    {
        SetPosition();
        Start();
    }

    public void Stop()
    {
        lock (instanceLocker)
        {
            if (!running) return;

            running = false;
            if (! turner.Join(250))
                turner.Abort();
        }
    }

    public void SetPosition()
    {
        SetPosition(Console.CursorLeft, Console.CursorTop);
    }

    public void SetPosition(int left, int top)
    {
        bool wasRunning;
        //prevent other start/stops during move
        lock (instanceLocker)
        {
            wasRunning = running;
            Stop();

            CursorLeft = left;
            CursorTop = top;

            if (wasRunning) Start();
        } 
    }

    public bool IsSpinning { get { return running;} }

    /* ---  PRIVATE --- */

    private int counter=-1;
    private Thread turner; 
    private bool running = false;
    private int rate = 100;
    private int CursorLeft;
    private int CursorTop;
    private Object instanceLocker = new Object();
    private static Object console = new Object();

    private void Turn()
    {
        while (running)
        {
            counter++;

            // prevent two instances from overlapping cursor position updates
            // weird things can still happen if the main ui thread moves the cursor during an update and context switch
            lock (console)
            {                  
                int OldLeft = Console.CursorLeft;
                int OldTop = Console.CursorTop;
                Console.SetCursorPosition(CursorLeft, CursorTop);

                switch (counter)
                {
                    case 0: Console.Write("/"); break;
                    case 1: Console.Write("-"); break;
                    case 2: Console.Write("\\"); break;
                    case 3: Console.Write("|"); counter = -1; break;
                }
                Console.SetCursorPosition(OldLeft, OldTop);
            }

            Thread.Sleep(rate);
        }
        lock (console)
        {   // clean up
            int OldLeft = Console.CursorLeft;
            int OldTop = Console.CursorTop;
            Console.SetCursorPosition(CursorLeft, CursorTop);
            Console.Write(' ');
            Console.SetCursorPosition(OldLeft, OldTop);
        }
    }

    public void Dispose()
    {
        Stop();
    }
}

샘플 코드는 내 것이 아니지만 좋은 수정입니다. Brad Abrams의 블로그에서 가져 왔습니다 (내 답변의 링크 참조). SetCursorPosition을 보여주는 간단한 샘플로 작성된 것 같습니다. Btw, 나는 간단한 샘플이라고 생각한 것에 대해 토론을 시작한 것에 대해 놀랐습니다. 이 사이트를 좋아하는 이유는 :-)입니다
더크 볼 마르

4

끝에 새 줄 (\ n)을 사용하는 대신 (암시 적 또는 명시 적으로) 줄의 시작 부분에 Carrage Return (\ r)을 명시 적으로 사용하면 원하는 것을 얻을 수 있습니다. 예를 들면 다음과 같습니다.

void demoPercentDone() {
    for(int i = 0; i < 100; i++) {
        System.Console.Write( "\rProcessing {0}%...", i );
        System.Threading.Thread.Sleep( 1000 );
    }
    System.Console.WriteLine();    
}

-1, 질문이 C #을 요청하고 C #으로 다시 작성하고 다시 F #으로 변경
Malfist

C #을 F #으로 다시 변경하지 않고 편집 충돌처럼 보입니다. 그의 변화는 당신이 끝나고 1 분이었고 스프린트에 중점을 두었습니다.
Andy

편집 해 주셔서 감사합니다. 나는 F # 대화식 모드를 사용하여 사물을 테스트하는 경향이 있으며 중요한 부분은 C #에서와 같은 BCL 호출이라고 생각했습니다.
James Hugard

3
    public void Update(string data)
    {
        Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
        Console.Write(string.Format("\r{0}", data));
    }

1

MSDN의 콘솔 문서에서 :

Out 또는 Error 속성의 TextWriter.NewLine 속성을 다른 줄 종료 문자열로 설정하여이 문제를 해결할 수 있습니다. 예를 들어 C # 문인 Console.Error.NewLine = "\ r \ n \ r \ n";은 표준 오류 출력 스트림에 대한 줄 종결 문자열을 두 개의 캐리지 리턴 및 줄 바꿈 시퀀스로 설정합니다. 그런 다음 C # 문에서와 같이 오류 출력 스트림 개체의 WriteLine 메서드를 명시 적으로 호출 할 수 있습니다. Console.Error.WriteLine ();

그래서-나는 이것을했다 :

Console.Out.Newline = String.Empty;

그런 다음 출력을 직접 제어 할 수 있습니다.

Console.WriteLine("Starting item 1:");
    Item1();
Console.WriteLine("OK.\nStarting Item2:");

거기에 도착하는 또 다른 방법.


NewLine 속성을 재정의하지 않고 같은 목적으로 Console.Write ()를 사용할 수 있습니다.
Radosław Gers

1

파일 생성을 멋지게 보이게하려면이 기능이 작동합니다.

                int num = 1;
                var spin = new ConsoleSpinner();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write("");
                while (true)
                {
                    spin.Turn();
                    Console.Write("\r{0} Generating Files ", num);
                    num++;
                }

그리고 이것은 아래의 답변에서 얻은 방법이며 수정했습니다.

public class ConsoleSpinner
    {
        int counter;

        public void Turn()
        {
            counter++;
            switch (counter % 4)
            {
                case 0: Console.Write("."); counter = 0; break;
                case 1: Console.Write(".."); break;
                case 2: Console.Write("..."); break;
                case 3: Console.Write("...."); break;
                case 4: Console.Write("\r"); break;
            }
            Thread.Sleep(100);
            Console.SetCursorPosition(23, Console.CursorTop);
        }
    }

0

또 다른 하나가 있습니다 : D

class Program
{
    static void Main(string[] args)
    {
        Console.Write("Working... ");
        int spinIndex = 0;
        while (true)
        {
            // obfuscate FTW! Let's hope overflow is disabled or testers are impatient
            Console.Write("\b" + @"/-\|"[(spinIndex++) & 3]);
        }
    }
}

0

한 줄을 업데이트하려고하지만 정보가 너무 길어서 한 줄에 표시 할 수없는 경우 새 줄이 필요할 수 있습니다. 이 문제가 발생했으며 아래에서이 문제를 해결할 수 있습니다.

public class DumpOutPutInforInSameLine
{

    //content show in how many lines
    int TotalLine = 0;

    //start cursor line
    int cursorTop = 0;

    // use to set  character number show in one line
    int OneLineCharNum = 75;

    public void DumpInformation(string content)
    {
        OutPutInSameLine(content);
        SetBackSpace();

    }
    static void backspace(int n)
    {
        for (var i = 0; i < n; ++i)
            Console.Write("\b \b");
    }

    public  void SetBackSpace()
    {

        if (TotalLine == 0)
        {
            backspace(OneLineCharNum);
        }
        else
        {
            TotalLine--;
            while (TotalLine >= 0)
            {
                backspace(OneLineCharNum);
                TotalLine--;
                if (TotalLine >= 0)
                {
                    Console.SetCursorPosition(OneLineCharNum, cursorTop + TotalLine);
                }
            }
        }

    }

    private void OutPutInSameLine(string content)
    {
        //Console.WriteLine(TotalNum);

        cursorTop = Console.CursorTop;

        TotalLine = content.Length / OneLineCharNum;

        if (content.Length % OneLineCharNum > 0)
        {
            TotalLine++;

        }

        if (TotalLine == 0)
        {
            Console.Write("{0}", content);

            return;

        }

        int i = 0;
        while (i < TotalLine)
        {
            int cNum = i * OneLineCharNum;
            if (i < TotalLine - 1)
            {
                Console.WriteLine("{0}", content.Substring(cNum, OneLineCharNum));
            }
            else
            {
                Console.Write("{0}", content.Substring(cNum, content.Length - cNum));
            }
            i++;

        }
    }

}
class Program
{
    static void Main(string[] args)
    {

        DumpOutPutInforInSameLine outPutInSameLine = new DumpOutPutInforInSameLine();

        outPutInSameLine.DumpInformation("");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");


        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

        //need several lines
        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

        outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbb");

    }
}

0

나는 vb.net에서 동일한 솔루션을 찾고 있었고 이것을 찾았고 훌륭합니다.

그러나 @JohnOdom은 이전 공백이 현재 공백보다 큰 경우 공백을 처리하는 더 좋은 방법을 제안했습니다.

vb.net에서 함수를 만들고 누군가 도움을받을 수 있다고 생각했습니다.

여기 내 코드가 있습니다 :

Private Sub sPrintStatus(strTextToPrint As String, Optional boolIsNewLine As Boolean = False)
    REM intLastLength is declared as public variable on global scope like below
    REM intLastLength As Integer
    If boolIsNewLine = True Then
        intLastLength = 0
    End If
    If intLastLength > strTextToPrint.Length Then
        Console.Write(Convert.ToChar(13) & strTextToPrint.PadRight(strTextToPrint.Length + (intLastLength - strTextToPrint.Length), Convert.ToChar(" ")))
    Else
        Console.Write(Convert.ToChar(13) & strTextToPrint)
    End If
    intLastLength = strTextToPrint.Length
End Sub

로컬 정적 변수의 VB 기능을 사용할 수 있습니다 Static intLastLength As Integer.
Mark Hurd

0

필자가 작성한 솔루션이 속도에 맞게 최적화 될 수 있는지 확인하기 위해 이것을 검색하고있었습니다. 내가 원하는 것은 현재 줄을 업데이트하는 것이 아니라 카운트 다운 타이머였습니다. 여기에 내가 생각해 낸 것이 있습니다. 누군가에게 유용 할 수 있습니다

            int sleepTime = 5 * 60;    // 5 minutes

            for (int secondsRemaining = sleepTime; secondsRemaining > 0; secondsRemaining --)
            {
                double minutesPrecise = secondsRemaining / 60;
                double minutesRounded = Math.Round(minutesPrecise, 0);
                int seconds = Convert.ToInt32((minutesRounded * 60) - secondsRemaining);
                Console.Write($"\rProcess will resume in {minutesRounded}:{String.Format("{0:D2}", -seconds)} ");
                Thread.Sleep(1000);
            }
            Console.WriteLine("");

0

@ E.Lahu Solution에서 영감을 받아 백분율로 막대 진행률을 구현합니다.

public class ConsoleSpinner
{
    private int _counter;

    public void Turn(Color color, int max, string prefix = "Completed", string symbol = "■",int position = 0)
    {
        Console.SetCursorPosition(0, position);
        Console.Write($"{prefix} {ComputeSpinner(_counter, max, symbol)}", color);
        _counter = _counter == max ? 0 : _counter + 1;
    }

    public string ComputeSpinner(int nmb, int max, string symbol)
    {
        var spinner = new StringBuilder();
        if (nmb == 0)
            return "\r ";

        spinner.Append($"[{nmb}%] [");
        for (var i = 0; i < max; i++)
        {
            spinner.Append(i < nmb ? symbol : ".");
        }

        spinner.Append("]");
        return spinner.ToString();
    }
}


public static void Main(string[] args)
    {
        var progressBar= new ConsoleSpinner();
        for (int i = 0; i < 1000; i++)
        {
            progressBar.Turn(Color.Aqua,100);
            Thread.Sleep(1000);
        }
    }

0

다음은 s soosh와 0xA3의 답변입니다. 스피너를 업데이트하는 동안 사용자 메시지로 콘솔을 업데이트 할 수 있으며 경과 시간 표시기도 있습니다.

public class ConsoleSpiner : IDisposable
{
    private static readonly string INDICATOR = "/-\\|";
    private static readonly string MASK = "\r{0} {1:c} {2}";
    int counter;
    Timer timer;
    string message;

    public ConsoleSpiner() {
        counter = 0;
        timer = new Timer(200);
        timer.Elapsed += TimerTick;
    }

    public void Start() {
        timer.Start();
    }

    public void Stop() {
        timer.Stop();
        counter = 0;
    }

    public string Message {
        get { return message; }
        set { message = value; }
    }

    private void TimerTick(object sender, ElapsedEventArgs e) {
        Turn();
    }

    private void Turn() {
        counter++;
        var elapsed = TimeSpan.FromMilliseconds(counter * 200);
        Console.Write(MASK, INDICATOR[counter % 4], elapsed, this.Message);
    }

    public void Dispose() {
        Stop();
        timer.Elapsed -= TimerTick;
        this.timer.Dispose();
    }
}

사용법은 다음과 같습니다.

class Program
{
    static void Main(string[] args)
    {
        using (var spinner = new ConsoleSpiner())
        {
            spinner.Start();
            spinner.Message = "About to do some heavy staff :-)"
            DoWork();
            spinner.Message = "Now processing other staff".
            OtherWork();
            spinner.Stop();
        }
        Console.WriteLine("COMPLETED!!!!!\nPress any key to exit.");

    }
}

-1

SetCursorPosition방법은 멀티 스레딩 시나리오에서 작동하며 다른 두 방법은 그렇지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.