C♯'s String.Format: A (hopefully) complete tutorial
For a while now whenever I've been formatting strings I've been using the String.Format() method that's built into C♯. Recently, I found it's documentation page, and found that I didn't know the half of what it was capable of, so I decided to learn all of it's tricks and write a tutorial for this blog at the same time. Firstly, here's an example of it in action:
int number = 4626;
string textLine = String.Format("The number is {0}", number);
Console.WriteLine(textLine);
You can pass a number of variables in after the string's format, and they can be referenced in the string itself through the curly braces syntax: {0}. 0 refers to the first variable, {1} to the second, etc. Anything you pass in will have it's .ToString() method called, so you can control how your classes are represented.
We can go one better than the above though. The Console.Write() and Console.WriteLine() methods have String.Format() functionality built in, so we can shorten the above down to 2 lines:
int number = 4626;
Console.WriteLine("The number is {0}", number);
Logging a table of data to the console? You can also pad any variable to make it a given length. Just add a comma and then the length that you want it to pad shorter strings to:
Random rng = new Random();
for(int i = 0; i < 100; i++)
{
Console.WriteLine("{0,4}: {1}", i, rng.Next());
}
The above will output something similar to this:
0: 1386044068
1: 337840762
2: 796579848
3: 2124870500
4: 1451010095
5: 2086322183
6: 2057125438
7: 1856321748
8: 1688522403
9: 982979897
10: 1571218679
11: 1851393877
12: 572762676
13: 549024616
14: 1496101994
...
By default, it pushes things to the right, but you can make it do the opposite by adding a minus sign to the padding length. So if you wanted to push the first variable to the left and have a minimum length of 4, it would be {0,-4}.
It doesn't stop there though. For numbers, you can convert can control the number of decimal places that are displayed. This is done like this:
Random rng = new Random();
for(int i = 0; i < 100; i++)
{
Console.WriteLine("{0,4}: {1:0.00}", i, rng.NextDouble());
}
Notice that we're using a colon instead of a comma here. The above will output something like this:
0: 0.86
1: 0.91
2: 0.97
3: 0.41
4: 0.70
5: 0.81
6: 0.26
7: 0.73
8: 0.93
9: 0.14
10: 0.80
11: 0.71
...
You can even automatically convert a number to a percentage by adding a percent sign (%), and it will automatically be multiplied by 100:
Random rng = new Random();
for(int i = 0; i < 100; i++)
{
Console.WriteLine("{0,4}: {1:0.00%}", i, rng.NextDouble());
}
Then you get an output like this:
0: 59.62%
1: 37.75%
2: 33.98%
3: 44.40%
4: 66.73%
5: 83.00%
6: 95.67%
7: 47.98%
8: 5.05%
9: 88.48%
If you want to ensure that all the values have the same width (and who wouldn't?), you can combine the two syntaxes like this:
Random rng = new Random();
for(int i = 0; i < 100; i++)
{
Console.WriteLine("{0,4}: {1,-8:0.00%}", i, rng.NextDouble());
}
The above produces something like this:
...
12: 36.60%
13: 60.48%
14: 13.96%
15: 36.88%
16: 6.55%
17: 89.51%
18: 19.18%
19: 91.98%
...
Numbers can also be converted to hex like this:
Random rng = new Random();
for(int i = 0; i < 100; i++)
{
Console.WriteLine("{0,4}: {1:x8}", i, rng.Next());
}
The above formats the numbers as lowercase hex, and pads them to be at least 8 characters long. Here's what the above might output:
0: 4d7867e8
1: 3dc7d06d
2: 118b1c4b
3: 5cd477c5
4: 580a74e1
5: 69d50a4b
6: 7ccf118e
7: 25725808
8: 093ddea4
9: 164a7baf
...
Simply change the lowercase x to an uppercase one to change the format to be uppercase. Numbers aren't the only thing that String.Format() lets you play with though. Dates are supported too:
DateTime dt = DateTime.Now;
Console.WriteLine("Day {0:d} of the month of {0:m} the {0:yyyy}th year", dt);
// Output example: Day 04/09/2015 of the month of 04 September the 2015th year
That pretty much concludes this post about String.Format(). If I tried to demonstrate every little feature, we would be here all year! Before I go though, I have put together a quick reference guide on all the different formats that it supports:
| Format | Example | Description |
|---|---|---|
{0} |
cheese |
The first variable |
{0,8} |
cheese |
Pad the first variable on the left to 8 characters |
{0,-8} |
cheese |
Pad the first variable on the right to 8 characters |
{0:0.00} |
4.38 |
Round the first variable to 2 decimal places |
{0,6:0.000} |
3.432 |
Round the first variable to 3 decimal places and then pad it on the left to 6 characters |
{0:x} |
efe9a |
Convert the first variable to lowercase hex |
{0:X8} |
EFE9A |
Convert the first variable to uppercase hex and then pad it on the left to 8 characters. |
{0:d} |
06/09/2015 |
Extract the date part of a DateTime object. |
{0:t} |
02:19pm |
Extract the time part of a DateTime object |
{0:hh} |
02 |
Extract the hour from a DateTime object |
{0:mm} |
19 |
Extract the minute from a DateTime object |
{0:ss} |
56 |
Extract the seconds from a DateTime object |
{0:dd} | 09 | Extract the day from a DateTime object
{0:MM} | 09 | Extract the month from a DateTime object
{0:M} | September | Extract the long month from a DateTime object
{0:yy} | 15 | Extract the short year from a DateTime object
{0:yyyy} | 2015 | Extract the long year from a DateTime object
Update 29th October 2015: A commenter by the name Tom let me know that in C♯ 6.0 and above (that's the .NET framework 4.6+) comes with another handy feature that lets you substitute in the contents of a variable directly. Here's his example:
int number = 4626;
Console.WriteLine("The number is {number}");
The above would output The number is 4626. This also extendes to properties of objects, letting you do something like this:
Console.WriteLine("Account {myAccount.Id} belongs to {myAccount.Name} and contains £{myAccount.Balance}.");
There's also a shorthand version of String.Format. Simply prefix a string with a dollar sign ($) and the string will be pushed through String.Format for you:
string display = $"Launch in T minus {rocket.Countdown} seconds!";
Console.WriteLine(display);
You can find out more here: New Language features of C# 6