当在c#中构建Windows控制台应用程序时,是否有可能写入控制台而不必扩展当前行或去到新行?例如,如果我想显示一个百分比,表示一个流程离完成有多近,我只需要在游标所在的同一行上更新该值,而不必将每个百分比放在新行上。

这可以用“标准”c#控制台应用程序来实现吗?


当前回答

如果你只打印“\r”到控制台,光标会回到当前行的开头,然后你可以重写它。这应该可以达到目的:

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

注意数字后面的几个空格,以确保之前的任何内容都被擦除。 还要注意使用Write()而不是WriteLine(),因为您不想在行末添加“\n”。

其他回答

来自MSDN的控制台文档:

您可以通过设置 TextWriter。属性的NewLine 属性转移到另一行 终止的字符串。例如, c#语句,Console.Error.NewLine = “\r\n\r\n”;,设置行终止 字符串用于标准错误输出 流到两车厢返回并行 饲料序列。然后你就可以 显式调用WriteLine方法 错误输出流对象的 在c#语句中, Console.Error.WriteLine ();

所以,我这样做了:

Console.Out.Newline = String.Empty;

然后我就可以自己控制输出了;

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

另一种方法。

你可以使用控制台。SetCursorPosition设置光标的位置,然后在当前位置写入。

下面是一个简单的“spinner”的例子:

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

灵感来自@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);
        }
    }

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