writeline - c# print




我如何更新C#Windows控制台应用程序中的当前行? (10)

SetCursorPosition方法适用于多线程场景,其他两种方法不适用

在C#中构建Windows控制台应用程序时,是否可以写入控制台而无需扩展当前行或转至新行? 例如,如果我想显示一个百分比来表示一个进程完成的距离,我只想更新与游标在同一行上的值,而不必将每个百分比都放在一个新行上。

这可以通过“标准”C#控制台应用程序完成吗?


\r用于这种情况。
\r 表示回车,表示光标返回到行首。
这就是为什么窗口使用\n\r因为它是新的行标记。
\n将您向下移动一行,然后\r将您返回到行首。


到目前为止,我们有三个相互竞争的方法来解决这个问题:

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引起了明显的闪烁,我没有观察到任何一种选择。 所以,道德是尽可能使用退后或回车谢谢教我更快的方式来做到这一点,所以!

更新 :在注释中,Joel建议SetCursorPosition在移动距离方面是恒定的,而其他方法是线性的。 进一步的测试证实了这种情况, 时间不变,速度缓慢仍然很慢。 在我的测试中,向控制台写入一长串退格比SetCursorPosition快,直到60个字符左右。 因此,用于替换短于60个字符(或大约60个字符)的行的部分更快, 并且它不闪烁,所以我将支持\ b通过\ r和SetCursorPosition最初认可。


在行的开头显式使用Carrage Return(\ r),而不是(隐式或显式地)在最后使用New Line(\ n)应该得到你想要的。 例如:

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

您可以使用Console.SetCursorPosition设置光标位置,然后在当前位置写入。

这是一个显示简单的“微调”的example

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 )。


您可以使用\ 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方法相比,这样做的好处在于,即使您的百分比输出不在线的起始位置,也能正常工作。


我在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

这是另一个: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]);
        }
    }
}

如果您仅在控制台上打印"\r" ,则光标会返回到当前行的开头,然后您可以重写它。 这应该可以做到这一点:

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

注意数字后面的几个空格,以确保之前的内容被删除。
另外请注意使用Write()而不是WriteLine()因为您不想在行尾添加“\ n”。


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




console