IVR*_*ger 479 c# windows console
在C#中构建Windows控制台应用程序时,是否可以写入控制台而无需扩展当前行或转到新行?例如,如果我想显示一个百分比表示一个进程完成的接近程度,我只想更新与光标在同一行的值,而不必将每个百分比放在一个新行上.
这可以通过"标准"C#控制台应用程序完成吗?
sho*_*osh 744
如果只打印"\r"到控制台,光标会返回到当前行的开头,然后您可以重写它.这应该做的伎俩:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Run Code Online (Sandbox Code Playgroud)
注意数字后面的几个空格,以确保删除之前的任何内容.
另请注意使用Write()而不是WriteLine()因为您不想在行的末尾添加"\n".
Dir*_*mar 242
您可以使用Console.SetCursorPosition设置光标的位置,然后在当前位置写入.
这是一个显示简单"微调器" 的示例:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,您必须确保使用新输出或空白覆盖任何现有输出.
更新:由于有人批评该示例仅将光标移回一个字符,我将添加此内容以澄清:使用SetCursorPosition您可以将光标设置为控制台窗口中的任何位置.
Console.SetCursorPosition(0, Console.CursorTop);
Run Code Online (Sandbox Code Playgroud)
将光标设置为当前行的开头(或者您可以Console.CursorLeft = 0直接使用).
Kev*_*vin 78
到目前为止,我们有三种竞争方案可供选择:
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
}
Run Code Online (Sandbox Code Playgroud)
我总是使用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);
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上,我得到以下结果:
此外,SetCursorPosition引起了明显的闪烁,我没有观察到任何一种替代方案.因此,道德是尽可能使用退格或回车,并感谢教我一个更快的方法来做到这一点,所以!
更新:在评论中,Joel建议SetCursorPosition相对于移动的距离是恒定的,而其他方法是线性的.进一步的测试证实了这种情况,然而恒定的时间和缓慢仍然很慢.在我的测试中,将一长串退格写入控制台比SetCursorPosition更快,直到大约60个字符.因此,退格会更快地替换短于60个字符(或左右)的部分行,并且它不会闪烁,所以我将支持我最初认可的\ b over\r \n和SetCursorPosition.
Sea*_*ean 27
您可以使用\ b(退格)转义序列来备份当前行上的特定数量的字符.这只是移动当前位置,它不会删除字符.
例如:
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);
}
Run Code Online (Sandbox Code Playgroud)
这里,line是写入控制台的百分比行.诀窍是为前一个输出生成正确数量的\ b字符.
这种优于\ r方法的优点是,即使您的百分比输出不在行的开头,也能正常工作.
Mal*_*ist 18
\r用于这些场景.
\r 表示回车符,表示光标返回到行的开头.
这就是Windows \n\r用作新线标记的原因.
\n让你向下移动一行,然后\r返回到行的开头.
Joe*_*orn 14
我只需要和divo的ConsoleSpinner课一起玩.我的距离很简洁,但是我觉得这个类的用户必须编写自己的while(true)循环.我正在拍摄更像这样的体验:
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
Run Code Online (Sandbox Code Playgroud)
我用下面的代码意识到了这一点.由于我不希望我的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();
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
194276 次 |
| 最近记录: |