В настоящее время у меня есть конфигурационный файл «шаблон» с добавленным расширением, например:
web.config.rename
Однако, я вижу проблему с этим методом, если были изменены критические изменения.
Если вы выводите на консоль только "\ r"
, курсор вернется в начало текущей строки, а затем вы сможете его переписать. Это должно помочь:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Обратите внимание на несколько пробелов после числа, чтобы убедиться, что все, что было до этого, было удалено.
Также обратите внимание на использование Write ()
вместо WriteLine ()
, поскольку вы не хотите добавлять "\ n" в конце строки.
Это работает, если Вы хотите заставить генерирующиеся файлы выглядеть прохладными.
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);
}
}
Явное использование возврата каретки (\ 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();
}
Мне просто пришлось поиграться с классом 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();
}
}
\ r
используется для этих сценариев.
\ r
представляет собой возврат каретки, что означает возврат курсора в начало строки.
Вот почему Windows использует \ n \ r
в качестве маркера новой строки.
\ n
перемещает вас вниз по строке, а \ r
возвращает вас в начало строки.
Вы можете использовать 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 работает, даже если ваш процентный вывод находится не в начале строки.
На данный момент у нас есть три конкурирующих варианта, как это сделать:
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);
}
На моем компьютере я получил следующие результаты:
Кроме того, SetCursorPosition
вызывал заметное мерцание, которое я не наблюдал ни с одной из альтернатив. Итак, мораль состоит в том, чтобы по возможности использовать обратные пробелы или возврат каретки , а спасибо за то, что научил меня более быстрому способу сделать это, SO!
Обновление : в Комментарии, Джоэл предполагает, что SetCursorPosition является постоянным по отношению к пройденному расстоянию, в то время как другие методы являются линейными. Дальнейшие испытания подтверждают, что это так, однако постоянное время и медленный процесс все еще медленный. В моих тестах запись длинной строки возврата на консоль выполняется быстрее, чем SetCursorPosition, примерно до 60 символов. Таким образом, backspace быстрее заменяет части строки короче 60 символов (или около того), и он не мерцает, поэтому я собираюсь поддержать свое первоначальное одобрение \ b вместо \ r и SetCursorPosition
.
SetCursorPosition
. запись длинной строки возврата на консоль выполняется быстрее, чем SetCursorPosition, примерно до 60 символов. Таким образом, backspace быстрее заменяет части строки короче 60 символов (или около того), и он не мерцает, поэтому я собираюсь поддержать свое первоначальное одобрение \ b вместо \ r и SetCursorPosition
. Если в вашем распоряжении Enterprise Edition SQL Server, могу я предложить вам использовать технологию секционирования SQL Server.
У вас могут быть текущие необходимые данные, находящиеся в «Live» раздел и обновленная версия данных во «Вторичном» разделе (который недоступен для запросов, а скорее для администрирования данных).
После того, как данные были импортированы во «Вторичный» раздел, вы можете мгновенно ПЕРЕКЛЮЧИТЬ «ЖИВОЙ» 'разделить OUT' и 'Secondary' раздел IN, тем самым обеспечивая нулевое время простоя и отсутствие блокировки.
После того, как вы сделали переключение, вы можете приступить к усечению больше не нужных данных, не затрагивая пользователей новых оперативных данных (ранее вторичный раздел).
Каждый раз, когда вам нужно выполнить задание импорта, Я добавлю это для пояснения: Используя SetCursorPosition
, вы можете установить курсор в любую позицию в окне консоли.
Console.SetCursorPosition(0, Console.CursorTop);
установит курсор в начало текущей строки (или вы можете напрямую использовать Console.CursorLeft = 0
).