A discussion of the .NET format specifier and how its used. The relationship between the components and a cheat card of the format specifier details.
.NET formattting, or why I miss sprintf
In the C programming language the sprintf function was one that let you format
values into strings. I can still type it out from memory.
sprintf(buffer,"Result is %04.4d",ival);
Assuming that ival was an integer variable this would format the value of ival into the char array called buffer.
The leading '0' means that the value is to be zero filled on the left. '4.4' means that
there the minimum length of the result is 4 and the maximum is 4. 'd' means that this is a numeric
value. The rest of the string, the text before the '%', would pass through as is.
If ival had the value 37 the buffer would have the value "Result is 0037".
Fast forward to the present, and welcome to the .NET formatting capability. Like a lot of the platform it is very full featured, and like
a lot of the platform its very complex. Only by setting aside several hours to write this
article and create these graphics have I begun to internalize it, and of course I'll lose that
knowledge if I don't use it.
If you've used .NET for very long, whether its VB or C#, you've come across the very cool
method that all variables and objects have, "ToString()", to let you quickly print out a formatted version
of the value. If you have dug deeper you have found that, for common variable types, ToString() can take a string
argument to format the value, somethign like this:
int ival = 37;
string s = ival.ToString("D4"); // produces "0037"
If you have explored further you probably have found
the built-in String class has a method
called "Format", which takes a string that lets you format one or more variables, something
like this:
DateTime now = System.DateTime.Now; // Assume that today is Thursday
string s = String.Format("Hey {0}, today is {1:dddd}", "john",now);
// result is "Hey john, today is Thursday"
If you're like me it has taken some time to figure out when you could specify the
form with the {} in it and when you could only specify a single value.
String.Format("D4",ival); // Not Correct!
ival.ToString("Your value is {0}"); // Not Correct!
Another mystery is why the same apparant format specifier does different things
depending on the type of the variable, and how the platform "knows" how to
apply certain formatting.
int ival = 37;
DateTime now = System.DateTime.Now;
string s = ival.ToString("D"); // produces "37"
string t = now.ToString("D"); // produces Thursday, 26 July 2007
The answer to the just asked question (why the same format specifier produces
different results) is that they are not, in fact, the same format specifier.
In addition, .NET knows the types of variables so it knows how to make decisions
based on that. In this example
.NET knows that the variable "ival" is an integer and so it brings in the
format specifiers that is specific to numbers. .NET also knows that the
variable "now" is of type datetime, so it brings in the datetime format specifiers.
The fact that there is a "D" for numbers and a "D" for datetimes is coincidence.
Its all in the docs, of course, but like all meaty Microsoft docs you get all of the information, which means
much more than you actually need to learn how to use it. This is inevitable and appropriate,
of course, I'm a huge fan of MSDN,
but I know that their audience is everyone, not just
people trying to learn how to use somethign. They have to target people writing controls, or doing
advanced work.
And so, here is a graphic that shows the relationship between the long form
of the format string and the short form, and where the format specifier ("D4" or "dddd")
fits in. Following the graphic there is a table that summarizes the format specifiers for each category. I've also created an online utility that can help with development of datetime custom format specifiers. Its located on this website
at this link.
| Format Specifiers |
|---|
| Standard Numeric |
Form is Ann, where A is one of
C D E F G N P R X and
nn is precision |
| Custom Numeric |
Assembled from of 0 # . , % E0 E+0 E-0 \ ; 'literal' |
| Standard DateTime |
One of d D f F g G M o R s t T u U Y |
| Custom DateTime |
Assembled from %c where c is one of
d f F g h H K m M s t y z
Can repeat specifiers for more precision (except K)
Can also contain / 'literal' \ : |
| Enumeration |
One of g f d x |
From this shortcut it should take you less time to get where you need to go.
As time goes on I will continue to develop this topic with more help and discussion.