使用C#构建Windows控制台应用程序时,是否可以在不扩展当前行或转到新行的情况下写入控制台? 例如,如果我想显示一个百分比,该百分比代表一个过程到完成为止的距离,我只想在与光标相同的行上更新值,而不必将每个百分比都放在一个新行上。

可以使用“标准” C#控制台应用程序完成此操作吗?


#1楼

如果要更新一行,但信息太长而无法在一行上显示,则可能需要一些新行。 我遇到了这个问题,下面是解决此问题的一种方法。

public class DumpOutPutInforInSameLine
{//content show in how many linesint TotalLine = 0;//start cursor lineint cursorTop = 0;// use to set  character number show in one lineint OneLineCharNum = 75;public void DumpInformation(string content){OutPutInSameLine(content);SetBackSpace();}static void backspace(int n){for (var i = 0; i < n; ++i)Console.Write("\b \b");}public  void SetBackSpace(){if (TotalLine == 0){backspace(OneLineCharNum);}else{TotalLine--;while (TotalLine >= 0){backspace(OneLineCharNum);TotalLine--;if (TotalLine >= 0){Console.SetCursorPosition(OneLineCharNum, cursorTop + TotalLine);}}}}private void OutPutInSameLine(string content){//Console.WriteLine(TotalNum);cursorTop = Console.CursorTop;TotalLine = content.Length / OneLineCharNum;if (content.Length % OneLineCharNum > 0){TotalLine++;}if (TotalLine == 0){Console.Write("{0}", content);return;}int i = 0;while (i < TotalLine){int cNum = i * OneLineCharNum;if (i < TotalLine - 1){Console.WriteLine("{0}", content.Substring(cNum, OneLineCharNum));}else{Console.Write("{0}", content.Substring(cNum, content.Length - cNum));}i++;}}}
class Program
{static void Main(string[] args){DumpOutPutInforInSameLine outPutInSameLine = new DumpOutPutInforInSameLine();outPutInSameLine.DumpInformation("");outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");//need several linesoutPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbb");}
}

#2楼

    public void Update(string data){Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));Console.Write(string.Format("\r{0}", data));}

#3楼

这是我对s 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.");}

#4楼

这是另一个: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 impatientConsole.Write("\b" + @"/-\|"[(spinIndex++) & 3]);}}
}

#5楼

SetCursorPosition方法可用于多线程方案,而其他两种方法则不能


#6楼

我在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 belowREM intLastLength As IntegerIf boolIsNewLine = True ThenintLastLength = 0End IfIf intLastLength > strTextToPrint.Length ThenConsole.Write(Convert.ToChar(13) & strTextToPrint.PadRight(strTextToPrint.Length + (intLastLength - strTextToPrint.Length), Convert.ToChar(" ")))ElseConsole.Write(Convert.ToChar(13) & strTextToPrint)End IfintLastLength = strTextToPrint.Length
End Sub

#7楼

我正在为此进行搜索,以查看我编写的解决方案是否可以针对速度进行优化。 我想要的是一个倒数计时器,而不仅仅是更新当前行。 这是我想出的。 对某人可能有用

            int sleepTime = 5 * 60;    // 5 minutesfor (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("");

#8楼

如果您要使生成的文件看起来很酷,则可以使用此方法。

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

#9楼

从MSDN中的控制台文档中:

您可以通过将Out或Error属性的TextWriter.NewLine属性设置为另一个行终止字符串来解决此问题。 例如,C#语句Console.Error.NewLine =“ \\ r \\ n \\ r \\ n”;将标准错误输出流的行终止字符串设置为两个回车和换行序列。 然后,可以像C#语句Console.Error.WriteLine();一样显式调用错误输出流对象的WriteLine方法。

所以-我这样做了:

Console.Out.Newline = String.Empty;

这样我就可以自己控制输出了;

Console.WriteLine("Starting item 1:");Item1();
Console.WriteLine("OK.\nStarting Item2:");

到达那里的另一种方式。


#10楼

您可以使用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);}
}

请注意,您必须确保用新的输出或空白覆盖任何现有的输出。

更新:由于批评该示例仅将光标向后移动一个字符,因此我将添加此内容以进行澄清:使用SetCursorPosition可以将光标设置为控制台窗口中的任何位置。

Console.SetCursorPosition(0, Console.CursorTop);

会将光标设置在当前行的开头(或者您可以直接使用Console.CursorLeft = 0 )。


#11楼

如果仅在控制台上打印"\\r" ,则光标将返回到当前行的开头,然后可以将其重写。 这应该可以解决问题:

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

请注意数字后的几个空格,以确保删除之前的内容。
还请注意使用Write()而不是WriteLine()因为您不想在行尾添加“ \\ n”。


#12楼

\\r用于这些情况。
\\r 表示回车,表示光标返回到行的开头。
这就是Windows使用\\n\\r作为其新行标记的原因。
\\n将您下移,而\\r返回到行首。


#13楼

在行的开头显式使用回车符(\\ 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();
}

#14楼

您可以使用\\ 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);
}

在此, line是要写入控制台的百分比行。 技巧是为先前的输出生成正确数量的\\ b字符。

相对于\\ r方法,此方法的优点是即使您的输出百分比不在行首也可以运行。


#15楼

到目前为止,我们有三种竞争方法可以做到这一点:

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 cursorConsole.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相对于移动的距离是恒定的,而其他方法是线性的。 进一步的测试确认情况确实如此, 但是恒定时间和慢速仍然很慢。 在我的测试中,向控制台写入一长串的退格键比SetCursorPosition快,直到大约60个字符为止。 因此,退格键可以更快地替换少于60个字符(或类似字符)的行的一部分, 并且不会闪烁,因此我将支持我对\\ b的最初认可,而不是\\ r和SetCursorPosition


#16楼

我只需要玩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 instancelock (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 movelock (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 switchlock (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 upint OldLeft = Console.CursorLeft;int OldTop = Console.CursorTop;Console.SetCursorPosition(CursorLeft, CursorTop);Console.Write(' ');Console.SetCursorPosition(OldLeft, OldTop);}}public void Dispose(){Stop();}
}

如何在C#Windows控制台应用程序中更新当前行?相关推荐

  1. 如何在.NET控制台应用程序中获取应用程序的路径?

    如何在控制台应用程序中找到应用程序的路径? 在Windows Forms中 ,我可以使用Application.StartupPath查找当前路径,但这似乎在控制台应用程序中不可用. #1楼 上面的答 ...

  2. 如何在golang http服务端程序中读取2次Request Body?(转)

    转自知乎:如何在golang http服务端程序中读取2次Request Body? - 知乎 提问: 在golang http服务端程序中,我想在真正处理Request Body之前将Body中的内 ...

  3. 列举窗体控制台应用程序中的3中控件_今天来点枯燥的,Visual C#的Windows窗体运行过程...

    我们上一期只是在windows窗体上放了三个控件,并编写了一段小程序,实现了触发窗体上的按钮,使得标签上的内容改变,并能够结束运行当中的程序: 然而,我们在编写代码窗口时,windows窗体自动嵌入了 ...

  4. asp.net razor html,从控制台应用程序中的ASP.NET Razor模板生成HTML的当前最佳解决方案是什么?...

    ServiceStack是用于呈现Razor视图页面的另一个选项. 尽管它已针对集成到ASP.NET或HttpListener Web Host中进行了优化(并提供了用于在目录中自动发现和注册视图页面 ...

  5. XML文件解析(在Windows环境MFC程序中,使用自带的MSXML6.dll解析)

    主要总结一下MSXML DOM接口的应用.DOM(Document Object Model) 是微软提供的处理XML文档的一个API标准库,我们可以将其理解为一组抽象了XML文档结构的接口. MSX ...

  6. 六一:如何在Datawhale开源学习小程序中管

    我们的组队学习马上就要开营了,本次组队学习与以往不同的是小程序中增加了队伍管理的功能. 为了方便大家组队,Datawhale的 六一同学 为大家准备了在Datawhale开源学习小程序中队伍管理的教程 ...

  7. 如何在Datawhale开源学习小程序中创建队伍?

    我们的组队学习马上就要开营了,本次组队学习与以往不同的是小程序中增加了组队的功能.为了方便大家组队,Datawhale的 六一同学 为大家准备了在Datawhale开源学习小程序中创建队伍的教程. S ...

  8. 如何在Android的相机应用程序中添加Google相册快捷方式

    Google Photos is arguably the best photo management app on the Play Store. It's intuitive and easy t ...

  9. .Net Core 2.1 通用主机(Core 在控制台应用程序中的应用)

    一.介绍 官方文档中说,Microsoft.AspNetCore.App 元包(ASP.NET Core 2.1 或更高版本)包含通用主机的Microsoft.Extensions.Hosting包, ...

最新文章

  1. Windows下Python环境搭建
  2. python语法详解大全_笔记:Python 语法要点汇总
  3. 施一公的另一位女弟子:4年8篇CNS论文,入选“未来女科学家”,未来有望比肩颜宁...
  4. mysql添加联合主键
  5. jenkins指定服务器地址,jenkins迁移新服务器(更换IP),webhook地址修改
  6. exec sql_EXEC SQL概述和示例
  7. 舆情监测系统成为网络利器
  8. 现代软件工程—构建之法---第四章:练习与讨论
  9. paip.c#.net未能找到任何资源
  10. 利用teigha制作dwg无单位块工具开发
  11. 大江大河未来10年:中国人改变命运的的七次机遇
  12. PMP知识点(十一、干系人管理)
  13. 万科企业宗旨、愿景与核心价值观
  14. 趁你年轻快来学学如何搭建一个小说网站,这里有超详细教程,快进来看看吧,错过了可不要后悔哟。
  15. wannacry作者捉到了吗_WannaCry爆发的根源原来是它?
  16. 快速识记会计中的借贷两方
  17. 【数据结构】- 几个步骤教你认识并实现一个链表之带头(哨兵位)双向循环链表(中)
  18. 矩阵乘法——矩阵快速幂
  19. 32位/64位操作系统的最大支持内存的空间
  20. Spring Cloud Gateway +Oauth2 +JWT+Vue 实现前后端分离RBAC权限管理

热门文章

  1. Activity生命周期回调是如何被回调的?
  2. 微信小程序 解决请求服务器手机预览请求不到数据的方法
  3. Android 5.0学习之ListView升级版RecyclerView
  4. android 使用广播监听网络状态
  5. 【Android UI设计与开发】第10期:顶部标题栏(一)ActionBar详细概述和简单示例
  6. (0066)iOS开发之UITableViewCell上子控件通过superView找对应的cell的探究
  7. [设计模式随意链接]——命令模式
  8. Redis总结(五)缓存雪崩和缓存穿透等问题
  9. OpenGL入门学习(十二) 【转】
  10. JavaScript 中一句话的思索:this是函数在执行时所处的作用域