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

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


当前回答

我做了一个搜索,看看我写的解决方案是否可以优化速度。我想要的是一个倒数计时器,而不仅仅是更新当前行。 这是我想到的。可能对某人有用

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

其他回答

以下是我对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.");

    }
}

我做了一个搜索,看看我写的解决方案是否可以优化速度。我想要的是一个倒数计时器,而不仅仅是更新当前行。 这是我想到的。可能对某人有用

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

在行首显式地使用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();    
}

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

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

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

\r用于这些场景。 \r表示回车,这意味着光标返回到行首。 这就是Windows使用\n\r作为新行标记的原因。 \n将您移动到一行,\r将您返回到行开头。