update - c# console write same position




How can I update the current line in a C# Windows Console App? (10)

\r is used for this scenarios.
\r represents a carriage return which means the cursor returns to the start of the line.
That's why windows uses \n\r as it's new line marker.
\n moves you down a line, and \r returns you to the start of the line.

When building a Windows Console App in C#, is it possible to write to the console without having to extend a current line or go to a new line? For example, if I want to show a percentage representing how close a process is to completion, I'd just like to update the value on the same line as the cursor, and not have to put each percentage on a new line.

Can this be done with a "standard" C# console app?


Explicitly using a Carrage Return (\r) at the beginning of the line rather than (implicitly or explicitly) using a New Line (\n) at the end should get what you want. For example:

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

Here is my take on s soosh's and 0xA3's answers. It can update the console with user messages while updating the spinner and has an elapsed time indicator aswell.

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();
    }
}

usage is something like this. 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.");

    }

Here's another one :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]);
        }
    }
}

I was doing a search for this to see if the solution I wrote could be optimised for speed. What I wanted was a countdown timer, not just updating the current line. Here's what I came up with. Might be useful to someone

            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("");

If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:

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

Notice the few spaces after the number to make sure that whatever was there before is erased.
Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.


So far we have three competing alternatives for how to do this:

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
}

I've always used Console.CursorLeft = 0, a variation on the third option, so I decided to do some tests. Here's the code I used:

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);
}

On my machine, I get the following results:

  • Backspaces: 25.0 seconds
  • Carriage Returns: 28.7 seconds
  • SetCursorPosition: 49.7 seconds

Additionally, SetCursorPosition caused noticeable flicker that I didn't observe with either of the alternatives. So, the moral is to use backspaces or carriage returns when possible, and thanks for teaching me a faster way to do this, SO!


Update: In the comments, Joel suggests that SetCursorPosition is constant with respect to the distance moved while the other methods are linear. Further testing confirms that this is the case, however constant time and slow is still slow. In my tests, writing a long string of backspaces to the console is faster than SetCursorPosition until somewhere around 60 characters. So backspace is faster for replacing portions of the line shorter than 60 characters (or so), and it doesn't flicker, so I'm going to stand by my initial endorsement of \b over \r and SetCursorPosition.


The SetCursorPosition method works in multi-threading scenario, where the other two methods don't


You can use the \b (backspace) escape sequence to backup a particular number of characters on the current line. This just moves the current location, it does not remove the characters.

For example:

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);
}

Here, line is the percentage line to write to the console. The trick is to generate the correct number of \b characters for the previous output.

The advantage of this over the \r approach is that if works even if your percentage output is not at the beginning of the line.


i was looking for same solution in vb.net and i found this one and it's great.

however as @JohnOdom suggested a better way to handle the blanks space if previous one is larger than current one..

i make a function in vb.net and thought someone could get helped ..

here is my code:

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






console