консоль - console.writeline c#




Как обновить текущую строку в приложении консоли Windows C#? (10)

Вот еще один: 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]);
        }
    }
}

При создании приложения Windows Console на C # можно ли писать на консоль без необходимости продления текущей строки или перехода к новой строке? Например, если я хочу показать процент, показывающий, насколько близок процесс к завершению, я просто хотел бы обновить значение в той же строке, что и курсор, и не должен накладывать каждый процент на новую строку.

Можно ли это сделать с помощью «стандартного» консольного приложения C #?


Вот мои ответы на sosh и 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();
    }
}

использование - вот что. класса Программа {

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

    }

Вы можете использовать escape-последовательность \ b (backspace) для резервного копирования определенного количества символов в текущей строке. Это просто перемещает текущее местоположение, оно не удаляет символы.

Например:

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

Здесь строка - это процентная строка для записи на консоль. Трюк состоит в том, чтобы генерировать правильное количество символов \ b для предыдущего вывода.

Преимуществом этого подхода является то, что если работает, даже если ваш процентный вывод не находится в начале строки.


До сих пор у нас есть три конкурирующих альтернативы для того, как это сделать:

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 вызвало заметное мерцание, которое я не наблюдал ни с одним из альтернатив. Итак, мораль заключается в том, чтобы использовать возможности возврата или возврата каретки, когда это возможно , и спасибо за то, что я научил меня более быстрый способ сделать это, ТАК!

Обновление . В комментариях Джоэл предполагает, что SetCursorPosition является постоянным по отношению к пройденному расстоянию, в то время как другие методы являются линейными. Дальнейшее тестирование подтверждает, что это так, однако постоянное время и медленное время все еще медленны. В моих тестах запись длинной строки backspaces на консоль выполняется быстрее, чем SetCursorPosition, где-то около 60 символов. Таким образом, backspace быстрее заменяет части строки длиной менее 60 символов (или около того), и она не мерцает, поэтому я буду стоять перед моим первоначальным одобрением \ b over \ r и SetCursorPosition .


Из документов консоли в MSDN:

Вы можете решить эту проблему, установив свойство TextWriter.NewLine свойства Out или Error в другую строку завершения строки. Например, оператор 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 работает в многопоточном сценарии, где другие два метода не


Я делал поиск для этого, чтобы узнать, может ли решение, которое я написал, оптимизировать для скорости. Я хотел был таймер обратного отсчета, а не только обновление текущей строки. Вот что я придумал. Может быть полезно кому-то

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

Я искал такое же решение в 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

\r используется для этих сценариев.
\r представляет собой возврат каретки, что означает, что курсор возвращается к началу строки.
Вот почему окна используют \n\r поскольку это новый маркер строки.
\n перемещает вас по строке, а \r возвращает вас к началу строки.


Если вы печатаете только "\r" на консоли, курсор возвращается к началу текущей строки, а затем вы можете переписать его. Это должно сделать трюк:

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

Обратите внимание на несколько пробелов после номера, чтобы убедиться, что все, что было до этого, будет стерто.
Также обратите внимание на использование Write() вместо WriteLine() поскольку вы не хотите добавлять «\ n» в конец строки.







console