C# Windows Console App의 현재 행을 업데이트하려면 어떻게 해야 합니까?
C#에서 Windows Console App을 빌드할 때 현재 회선을 연장하거나 새 회선으로 이동하지 않고 콘솔에 쓸 수 있습니까?예를 들어 프로세스가 완료되기까지의 거리를 백분율로 표시하는 경우 커서와 같은 줄에 값을 업데이트하고 각 백분율을 새 줄에 표시할 필요가 없습니다.
이 작업은 "표준" C# 콘솔 앱에서 수행할 수 있습니까?
"\r"
커서가 현재 행의 선두로 돌아가 다시 쓸 수 있습니다.이렇게 하면 효과가 있습니다.
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
숫자 뒤에 공백이 몇 개 있으면 이전에 있던 것이 지워집니다.
, 「」, 「」의 사용법에 .Write()
WriteLine()
'\n'은 '\n'으로 하겠습니다.
하시면 됩니다.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
★★★★★★★★★★★★★★★★★★」
지금까지 NAT은 이 방법에 대해 세 가지 경쟁사 대안을 제시했습니다.
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초
- Set Cursor Position : 49.7초
★★★★★SetCursorPosition
눈에 띄는 깜박임을 일으켰지만 다른 방법으로는 관찰하지 못했습니다.가능한 한 백스페이스나 캐리지 리턴을 이용하는 것이 교훈입니다.이렇게 빨리 할 수 있는 방법을 가르쳐 주셔서 감사합니다.
업데이트: 코멘트에서 Joel은 SetCursorPosition이 이동 거리에 대해 일정하고 다른 메서드는 선형이라고 제안합니다.추가 테스트 결과, 이것이 사실로 확인되었지만, 일정한 시간과 느린 속도는 여전히 느립니다.테스트에서는 콘솔에 긴 백스페이스 스트링을 쓰는 것이 SetCursorPosition보다 60자 정도 빠릅니다.즉, 백스페이스는 60자 미만의 행의 일부를 교체하는 것이 빠르고(또는 그 정도), 깜박거리지 않기 때문에 \r에 대한 \b의 초기 보증은 그대로 두겠습니다.SetCursorPosition
.
\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 어프로치에 비해 이 방법의 장점은 % 출력이 행의 선두에 없는 경우에도 가 동작한다는 것입니다.
\r
아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 맞다.
\r
는 캐리지 리턴을 나타냅니다.이것은 커서가 줄의 선두로 돌아간다는 것을 의미합니다.
는 「Windows」를 사용하고 있습니다.\n\r
새로운 라인 마커로 사용합니다.
\n
하면, 「」가 됩니다.\r
행의 선두로 돌아갑니다.
그냥 디보네를 가지고 놀아야만 했어ConsoleSpinner
제 강의는 전혀 간결하지 않지만, 그 강의의 사용자들이 직접 써야 한다는 것이 마음에 들지 않았습니다.while(true)
이런 경험을 하고 싶어서요
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
그리고 아래 코드로 깨달았습니다.는 내 가원 since my my my since since since since since since since since since since since since since since since 를 원하지 않기 때문에Start()
차단 방법, 사용자가 파일 작성에 대해 걱정할 필요가 없습니다.while(spinFlag)
루프, 개의 할 수 스피너로 을 처리할 수 있는 를 따로 스레드를 생성해야 합니다.는말는는는는는는는는는는는는는는는는는는는는는는는는
또, 멀티 스레드는 그다지 실시하지 않았기 때문에, 거기에 미묘한 버그를 3개 남겼을 가능성이 있습니다.하지만 아직까지는 꽤 효과가 있는 것 같습니다.
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();
}
}
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
(암시적 또는 명시적으로) 새로운 회선(\n)을 사용하는 것이 아니라 회선의 선두에 캐러지 리턴(\r)을 명시적으로 사용하면 원하는 것을 얻을 수 있습니다.예를 들어 다음과 같습니다.
void demoPercentDone() {
for(int i = 0; i < 100; i++) {
System.Console.Write( "\rProcessing {0}%...", i );
System.Threading.Thread.Sleep( 1000 );
}
System.Console.WriteLine();
}
MSDN의 콘솔 문서에서 다음을 수행합니다.
TextWriter를 설정하면 이 문제를 해결할 수 있습니다.[ Out ]속성 또는 [Error]속성의 [NewLine]속성을 다른 줄 끝 문자열로 지정합니다.예를 들어 C# 문, Console 등이 있습니다.Error.NewLine = "\r\n\r\n"; 표준 오류 출력 스트림의 회선 종단 문자열을 2개의 캐리지 리턴 시퀀스와 회선 피드 시퀀스로 설정합니다.그런 다음 C# 문과 같이 오류 출력 스트림 객체의 WriteLine 메서드를 Console로 명시적으로 호출할 수 있습니다.Error.WriteLine();
그래서 이렇게 했어요.
Console.Out.Newline = String.Empty;
그러면 출력을 직접 제어할 수 있습니다.
Console.WriteLine("Starting item 1:");
Item1();
Console.WriteLine("OK.\nStarting Item2:");
거기 가는 또 다른 방법이지
이것은, 파일의 생성을 쿨하게 하고 싶은 경우에 유효합니다.
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);
}
}
여기 또 있습니다.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]);
}
}
}
한 줄을 업데이트하고 싶은데 정보가 너무 길어서 한 줄에 표시할 수 없는 경우 새 줄이 필요할 수 있습니다.저는 이 문제를 겪었습니다.이 문제를 해결할 수 있는 한 가지 방법은 다음과 같습니다.
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");
}
}
저는 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
제가 작성한 솔루션이 속도에 맞게 최적화될 수 있는지 알아보기 위해 이것을 검색했습니다.제가 원했던 건 카운트다운 타이머였어요.그냥 현재의 회선을 갱신하는 게 아니라.내가 생각해낸 건 이거야누군가에게 도움이 될 수도 있다
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("");
@E에서 영감을 얻었다.라후 솔루션, 바의 진행률을 퍼센티지와 함께 구현합니다.
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);
}
}
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.");
}
}
SetCursorPosition
두 방법이시나리오에서합니다.
언급URL : https://stackoverflow.com/questions/888533/how-can-i-update-the-current-line-in-a-c-sharp-windows-console-app
'programing' 카테고리의 다른 글
사전, 컬렉션 및 배열 비교 (0) | 2023.04.13 |
---|---|
콘솔에서 CSV 생성/열기 - 파일이 잘못된 형식 오류입니다. (0) | 2023.04.13 |
단말기의 현재 회선을 클리어/삭제하려면 어떻게 해야 하나요? (0) | 2023.04.13 |
Openpyxl - Python에서 Excel 파일에서 하나의 열만 읽는 방법은 무엇입니까? (0) | 2023.04.13 |
Bash 스크립트 파라미터 (0) | 2023.04.13 |