Как я могу обновить текущую строку в C # Windows Console App?

В настоящее время у меня есть конфигурационный файл «шаблон» с добавленным расширением, например:

web.config.rename

Однако, я вижу проблему с этим методом, если были изменены критические изменения.

463
задан GEOCHET 21 May 2009 в 13:15
поделиться

8 ответов

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

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

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

743
ответ дан 22 November 2019 в 22:47
поделиться

Это работает, если Вы хотите заставить генерирующиеся файлы выглядеть прохладными.

                int num = 1;
                var spin = new ConsoleSpinner();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write("");
                while (true)
                {
                    spin.Turn();
                    Console.Write("\r{0} Generating Files ", num);
                    num++;
                }

И это - метод, который я получил из некоторого ответа ниже и изменил его

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;
                case 4: Console.Write("\r"); break;
            }
            Thread.Sleep(100);
            Console.SetCursorPosition(23, Console.CursorTop);
        }
    }
1
ответ дан 22 November 2019 в 22:47
поделиться

Явное использование возврата каретки (\ r) в начале строки, а не (неявно или явно) использование новой строки (\ n) в конце должно дать желаемое. Например:

void demoPercentDone() {
    for(int i = 0; i < 100; i++) {
        System.Console.Write( "\rProcessing {0}%...", i );
        System.Threading.Thread.Sleep( 1000 );
    }
    System.Console.WriteLine();    
}
4
ответ дан 22 November 2019 в 22:47
поделиться

Мне просто пришлось поиграться с классом divo ConsoleSpinner . Моя не так кратка, но меня просто не устраивало, что пользователи этого класса должны писать свой собственный цикл while (true) . Я стремлюсь к более похожему опыту:

static void Main(string[] args)
{
    Console.Write("Working....");
    ConsoleSpinner spin = new ConsoleSpinner();
    spin.Start();

    // Do some work...

    spin.Stop(); 
}

И я реализовал это с помощью кода ниже. Поскольку я не хочу, чтобы мой метод Start () блокировался, я не Я не хочу, чтобы пользователю приходилось беспокоиться о написании цикла, подобного while (spinFlag) , и я хочу разрешить несколько прядильщиков одновременно, мне пришлось создать отдельный поток для обработки вращения. А это значит, что код должен быть намного сложнее.

Кроме того, я не использовал столько многопоточности, так что вполне возможно (даже если) я оставил там одну или три тонких ошибки. Но пока кажется, что это работает довольно хорошо:

public class ConsoleSpinner : IDisposable
{       
    public ConsoleSpinner()
    {
        CursorLeft = Console.CursorLeft;
        CursorTop = Console.CursorTop;  
    }

    public ConsoleSpinner(bool start)
        : this()
    {
        if (start) Start();
    }

    public void Start()
    {
        // prevent two conflicting Start() calls ot the same instance
        lock (instanceLocker) 
        {
            if (!running )
            {
                running = true;
                turner = new Thread(Turn);
                turner.Start();
            }
        }
    }

    public void StartHere()
    {
        SetPosition();
        Start();
    }

    public void Stop()
    {
        lock (instanceLocker)
        {
            if (!running) return;

            running = false;
            if (! turner.Join(250))
                turner.Abort();
        }
    }

    public void SetPosition()
    {
        SetPosition(Console.CursorLeft, Console.CursorTop);
    }

    public void SetPosition(int left, int top)
    {
        bool wasRunning;
        //prevent other start/stops during move
        lock (instanceLocker)
        {
            wasRunning = running;
            Stop();

            CursorLeft = left;
            CursorTop = top;

            if (wasRunning) Start();
        } 
    }

    public bool IsSpinning { get { return running;} }

    /* ---  PRIVATE --- */

    private int counter=-1;
    private Thread turner; 
    private bool running = false;
    private int rate = 100;
    private int CursorLeft;
    private int CursorTop;
    private Object instanceLocker = new Object();
    private static Object console = new Object();

    private void Turn()
    {
        while (running)
        {
            counter++;

            // prevent two instances from overlapping cursor position updates
            // weird things can still happen if the main ui thread moves the cursor during an update and context switch
            lock (console)
            {                  
                int OldLeft = Console.CursorLeft;
                int OldTop = Console.CursorTop;
                Console.SetCursorPosition(CursorLeft, CursorTop);

                switch (counter)
                {
                    case 0: Console.Write("/"); break;
                    case 1: Console.Write("-"); break;
                    case 2: Console.Write("\\"); break;
                    case 3: Console.Write("|"); counter = -1; break;
                }
                Console.SetCursorPosition(OldLeft, OldTop);
            }

            Thread.Sleep(rate);
        }
        lock (console)
        {   // clean up
            int OldLeft = Console.CursorLeft;
            int OldTop = Console.CursorTop;
            Console.SetCursorPosition(CursorLeft, CursorTop);
            Console.Write(' ');
            Console.SetCursorPosition(OldLeft, OldTop);
        }
    }

    public void Dispose()
    {
        Stop();
    }
}
14
ответ дан 22 November 2019 в 22:47
поделиться

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

18
ответ дан 22 November 2019 в 22:47
поделиться

Вы можете использовать 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 для предыдущего вывода.

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

27
ответ дан 22 November 2019 в 22:47
поделиться

На данный момент у нас есть три конкурирующих варианта, как это сделать:

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

На моем компьютере я получил следующие результаты:

  • Backspaces: 25,0 секунд
  • Возврат каретки: 28,7 секунды
  • SetCursorPosition: 49,7 секунды

Кроме того, SetCursorPosition вызывал заметное мерцание, которое я не наблюдал ни с одной из альтернатив. Итак, мораль состоит в том, чтобы по возможности использовать обратные пробелы или возврат каретки , а спасибо за то, что научил меня более быстрому способу сделать это, SO!


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

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

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

78
ответ дан 22 November 2019 в 22:47
поделиться

Если в вашем распоряжении Enterprise Edition SQL Server, могу я предложить вам использовать технологию секционирования SQL Server.

У вас могут быть текущие необходимые данные, находящиеся в «Live» раздел и обновленная версия данных во «Вторичном» разделе (который недоступен для запросов, а скорее для администрирования данных).

После того, как данные были импортированы во «Вторичный» раздел, вы можете мгновенно ПЕРЕКЛЮЧИТЬ «ЖИВОЙ» 'разделить OUT' и 'Secondary' раздел IN, тем самым обеспечивая нулевое время простоя и отсутствие блокировки.

После того, как вы сделали переключение, вы можете приступить к усечению больше не нужных данных, не затрагивая пользователей новых оперативных данных (ранее вторичный раздел).

Каждый раз, когда вам нужно выполнить задание импорта, Я добавлю это для пояснения: Используя SetCursorPosition , вы можете установить курсор в любую позицию в окне консоли.

Console.SetCursorPosition(0, Console.CursorTop);

установит курсор в начало текущей строки (или вы можете напрямую использовать Console.CursorLeft = 0 ).

242
ответ дан 22 November 2019 в 22:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: