|
I was just thinking, there's gotta be a way to do this, and then I run across your genius solution, and I'm super happy. Legit props on this code. One thing I wanted add is the ability to inject a message into the status, in case you want to provide additional details, so I threw this in with your already awesome code, and wanted to share it back with you.
static class ConsoleUtility
{
const char _block = '■';
const string _back = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
const string _twirl = "-\\|/";
public static void WriteProgressBar(int percent, bool update = false, string message = "")
{
if (update)
{
Console.Write(_back);
for (int x = 0; x <= message.Length; x++)
{
Console.Write("\b");
}
}
Console.Write("[");
var p = (int)((percent / 10f) + .5f);
for (var i = 0; i < 10; ++i)
{
if (i >= p)
Console.Write(' ');
else
Console.Write(_block);
}
Console.Write("] {0,3:##0}% {1}", percent, message);
}
public static void WriteProgress(int progress, bool update = false, string message = "")
{
if (update)
{
Console.Write("\b");
for (int x = 0; x <= message.Length; x++)
{
Console.Write("\b");
}
}
Console.Write($"{_twirl[progress % _twirl.Length]} {message}");
}
}
Here's a sample listing from the Main():
static void Main(string[] args)
{
ConsoleUtility.WriteProgressBar(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgressBar(i, true, $"testing {i}");
Thread.Sleep(50);
}
Console.WriteLine();
ConsoleUtility.WriteProgress(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgress(i, true, $"testing {i}");
Thread.Sleep(50);
}
}
|
|
|
|
|
Cool beans, thanks!
To err is human. Fortune favors the monsters.
|
|
|
|
|
Several years ago, I wrote and published
Console Magic, Part 1: Messages in Living Color, which had a somewhat more ambitious objective, which was to report progress as descriptive text, such as "processing entries for state TX," the idea being that most of my tasks have indeterminate sizes that preclude computing percentage complete.
While your routine cleverly covers your more modest use case, I suspect it will suffer from the same deficiency that afflicted the first version of my library. Although the method worked fine in a real console, it made a mess when the output was redirected to a file. Hence, I devised a method to determine whether the output was redirected. When it is, the progress messages are suppressed, since they contribute nothing useful to the redirected output.
With all that said, I think you can safely claim to cast spells in C#, too.
David A. Gray
Delivering Solutions for the Ages, One Problem at a Time
Interpreting the Fundamental Principle of Tabular Reporting
|
|
|
|
|
I understand what you're saying and I should have spit to STDERR which won't get redirected to a file (only STDOUT does)
Real programmers use butterflies
|
|
|
|
|
Actually, both can be redirected. If you do 2> MyErrors.TXT , STDERR goes to MyErrors.TXT .
David A. Gray
Delivering Solutions for the Ages, One Problem at a Time
Interpreting the Fundamental Principle of Tabular Reporting
|
|
|
|
|
True, but who does that? (i'm joking - mostly)
Real programmers use butterflies
|
|
|
|
|
I've redirected STDERR a time or two, though usually to test a feature.
Seeing "Real programmers use butterflies" in your signature suggests that you will appreciate the sign my wife and I keep in our office. It says "We don't believe in miracles. We rely upon them."
David A. Gray
Delivering Solutions for the Ages, One Problem at a Time
Interpreting the Fundamental Principle of Tabular Reporting
|
|
|
|
|
If I did it and it came out bad because a of a progress bar, I wouldn't be surprised or too upset. I'm not sure if my attitude reflects that of someone who might typically redirect STDERR or not. But in my mind, STDERR is usually all the interactive messages you don't want sent to a file or a pipe or what have you. Then again, that comes from years of console coding and using and it's just an experiential thing i picked up.
Your alternative sounds interesting, but it would have to be super elegant, compact and as cross platform as this code is for it to really make me consider it here, no offense.
It's just that I'm a staunch proponent of keeping things as absolutely simple as possible (but no simpler!) and i'd personally draw the line just after writing to STDERR
I'll update my code when i get to it, as honestly it was just an oversight on my part, and I should have known better.
Real programmers use butterflies
|
|
|
|
|
The simplicity of your approach is the reason I gave your article 5 stars. It's not often that you see a Code Project article that doesn't need a supporting download.
I had a very specific use case in mind in which capturing STDERR is important. When a console program runs as part of a scheduled task in a different user context (such as the SYSTEM account), it must be able to save anything critical that gets written to STDERR. Progress messages are incidental, and usually meaningless in that context.
I've written hundreds of console-mode programs, in VBScript, Perl, Python, C, C++, C#, and even assembler. Though most run in visible console windows, I've had a fair number of them that were destined to run in hidden consoles, mostly belonging to the SYSTEM account.
Bear in mind, too, that my use case involved strings of text, which got very messy when they found their way into a redirected stream file.
With all that said, on the whole, I agree with you. Where our viewpoints may differ is that I may start with that dead-simple use case, but, more often than not, I find new uses that break it, and the simple implementation that met the original use case gives way to a very robust implementation that can handle just about anything that you throw at it. That robust implementation almost always becomes part of a write-and-forget-about-it DLL.
Progress logging is a case in which that happened, and I rewrote the routine shortly after the publication of that article, 5 years ago. It's now part of a library that I put into an open source project, GitHub - txwizard/ConsoleAppAids3: Helper Classes for Character Mode Programs that Target the Microsoft .NET Framework 3.5 and Above. Since then, that method is essentially unchanged, although I eventually devised a way to eliminate the native code dependency in another library upon which it relies to determine when a stream is redirected. The library that contains that function is available on its own as Nuget package NuGet Gallery | WizardWrx.ConsoleStreams 7.15.191.37663.
The dependency is in GetStandardHandleState() , a static method on Class StandardHandleInfo, which is available separately as a like-named NuGet package, one of 11 closely related libraries that comprise my GitHub - txwizard/WizardWrx_NET_API.
Though I quit actively programming this past August, I still maintain these libraries, and they are in daily use, as tools, in my new work. Some were distributed to clients who were actively using them up until late last year.
In closing, real programmers use butterflies and miracles.
David A. Gray
Delivering Solutions for the Ages, One Problem at a Time
Interpreting the Fundamental Principle of Tabular Reporting
|
|
|
|
|
Thank you for the vote! And thanks for that expansive reply.
I actually agree with you in terms of incremental or iterative complexity over time.
I think for some of my console applications I may have to look into your library.
Particularly I do a lot of code generation, and sometimes that means Unicode strings get dumped from the program. Those get cooked horribly in the console, so detecting that and generating alternate (maybe not as compact) code could be useful to allow the app to still be able to be piped.
Real programmers use butterflies
|
|
|
|
|
While I'm not sure what my libraries have to offer specifically around Unicode string conversion, I suspect you'll find a lot to like in it and it bigger companion, the GitHub - txwizard/WizardWrx_NET_API, a large part of which comes into a project that imports https://www.nuget.org/packages/WizardWrx.ConsoleAppAids3/. I suggest at some point that you have a look at the API documentation at WizardWrx .NET API, because a lot is packed into the eleven or so libraries that comprise the set.
David A. Gray
Delivering Solutions for the Ages, One Problem at a Time
Interpreting the Fundamental Principle of Tabular Reporting
|
|
|
|
|
Snazzy - I now have a progress bar in my console apps that do updates to the DB from a large CSV file!
|
|
|
|
|
Slight improvement. I use this to erase them when I'm done. =) then i can overwrite them with the final status
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace CU
{
#if CULIB
public
#endif
static class ConsoleUtility
{
const char _block = '■';
const string _back = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
const string _twirl = "-\\|/";
public static void WriteProgressBar(int percent,bool update = false)
{
if(update)
Console.Write(_back);
Console.Write("[");
var p = (int)((percent / 10f)+.5f);
for (var i = 0;i<10;++i)
{
if (i >= p)
Console.Write(' ');
else
Console.Write(_block);
}
Console.Write("] {0,3:##0}%", percent);
}
public static void EraseProgressBar()
{
Console.Write(_back + new string(' ', _back.Length) + _back);
}
public static void WriteProgress(int progress, bool update = false)
{
if (update)
Console.Write("\b");
Console.Write(_twirl[progress % _twirl.Length]);
}
public static void EraseProgress()
{
Console.Write("\b \b");
}
}
}
Anyway, I'm glad you found it useful.
Real programmers use butterflies
|
|
|
|
|
I can hardly wait to add that little extra polish to my console apps with this progress indicator! I'll watch for the Mac test to make sure I grab the latest version of code. Thanks!
|
|
|
|
|
You're quite welcome.
I don't have a Mac to test on, but my backspace method should work on all platforms, so it's ready to use as is.
Later, if the Mac will accept it, I'll make a small change to the code, but I'm not looking to test it on a mac - i simply will if i get an opportunity.
That small change isn't really worth redownloading unless your progress bar flickers too much which it shouldn't.
Edit: I forgot I'm not changing this code because the \r would change the behavior of it. This should work on mac as is.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
There are not any reasons to use float instead of double:
Original code:
var p = (int) ((percent / 10f) + .5f);
C# way:
var p = (int) (percent / 10.0 + 0.5);
But in fact, we need only this:
var p = percent / 10;
And by the way, we can do all very easy, for example, so:
for (var i = 0; i <= 100; ++i)
{
const char _block = '■';
var s = string.Empty;
Console.SetCursorPosition(0,0);
var pos = i / 10;
Console.WriteLine($"[{s.PadLeft(pos,_block)}{s.PadRight(10 - pos, ' ')}] {i:000}%");
Thread.Sleep(50);
}
Enjoy C#!
|
|
|
|
|
Except now you haven't rounded away from zero. Frankly I only used float because i didn't need a double. It doesn't change the output either way, but your removal of the rounding will - or at least i'm fairly certain it will.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
Frankly I only used float because i didn't need a double.
No problem, in the C # world there are a lot of newbies who think so about the float type. They simply don’t know that the main floating-point type in the C # programming language is the double-precision type, and using floating-point numbers gives no some profit. And double type works faster than float at least.
For number round correctly you free to use the methods of the Math class.
I just wanted to help you.
Learn and enjoy C#!
|
|
|
|
|
I know quite a bit about C# as I have written several parser generators, and I worked on the Visual Studio Whidbey team way back when, writing codegen tools for VStudio.
For most of my career I've done backend server environment development (most often i think ISAPI C++)
That said, I've spent since dnf 1.1's release coding in C#
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
and I worked on the Visual Studio Whidbey team way back when, writing codegen tools for VStudio.
See told you were cool as f%&k
|
|
|
|
|
That was years ago. I'm less cool now.
hack everything.
|
|
|
|
|
For p you could use any int or even char if we make sure that percent is not more than 100.
uint8_t p;
p = percent / 10;
if(percent % 10) p++;
modified 10-Jan-20 11:55am.
|
|
|
|
|
that won't round though.
Mine rounds so 34% is 3 blocks and 35% is 4 blocks
hack everything.
|
|
|
|
|
You can use
const string _back = "\r";
to always set the cursor back to the beginning of the current line.
The next output will overwrite the old text.
|
|
|
|
|
I wasn't sure that would work in say, a linux console or not, which is why I went with backspace.
Once my dev machine is up and running again (down due to SNAFU tonight, i'll check) as I'm installing linux again (was on windows now to run it in a VM)
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|