String Formatting in C#
String formatting is handy to save on both code and garbage collection - it saves on code because it handles formatting including localisation in a way which would be tedious to reproduce manually, and on garbage collection because it uses a StringBuilder internally to perform its magic. This is a quick reference for the different types of formatting offered by the String.Format() method.
Basics
String formatting occurs via character subsitution: control characters in the string to be formatted are replaced with the arguments you provide it as such:
String.Format("{0} says hello", "John");
-> "John says hello"
Here, {0}
is substituted with the argument provided - you can pass up to three arguments or an argument array:
String.Format("{0} from {1} says hello to {2}", "John", "Sheffield", "Jane"); String.Format("{0} from {1} says hello to {2} from {3}", new object[]{"John", "Sheffield", "Jane", "Hull"});
The parameters to be substituted (e.g. {0}
) are formatted as such:
{<index>[,<alignement>][:<format specifier>]}
Where:
index
is the index of the object in the argument array to be used in the substitutionpadding
determines whether the substituted string should be padded to be a certain length- the
format specifier
allows the use of predefined formatting for the parameter
The .Net Framework provides standard specifiers depending on the type of the argument, as well as the ability to perform custom formatting for numeric and DateTime data types. The following specifiers can be used:
Numerals - Standard Formatting
The following standard specifiers are available for formatting numbers:
Specifier | Name | Syntax | Output (using double 9.8765) |
Output (using int -98765) |
---|---|---|---|---|
c (or C) | Currency | {0:c} | £9.88 | -£98,765.00 |
d (or D) | Decimal (whole number) |
{0:d} | System.FormatException | -98765 |
e (or E) | Exponent | {0:e} | 9.876500e+000 | -9.876500e+004 |
f (or F) | Fixed point | {0:f} | 9.88 | -98765.00 |
g (or G) | General | {0:g} | 9.8765 | -98765 |
n (or N) | Number | {0:n} | 9.88 | -98,765.00 |
r (or R) | Round trippable | {0:r} | 9.8765 | System.FormatException |
x (or X) | Hexadecimal | {0:x} | System.FormatException | fffe7e33 |
The currency specifiers is culture-dependent, and output will depend on the Locale of the current thread - the example outputs come from my box running with the “en-UK” locale, but would be different when run on another locale.
Numerals - Custom Formatting
In addition to the standard date specifiers, the following specifiers can be used for custom number formatting:
Specifier | Name | Syntax | Output (using double 9.8765) |
Output (using int -98765) |
---|---|---|---|---|
0 | Zero Placeholder | {0:00.0000} | 09.8765 | -98765.0000 |
# | Digit Placeholder | {0:##.##} | 9.88 | -98765 |
. | Decimal Point Separator | {0:#.#} | 9.9 | -98765 |
, | Thousands Separator | {0:#,#} | 10 | -98,765 |
% | Percentage | {0:#%} | 988% | -9876500% |
e (or E) | Exponent Placeholder | {0:00e+000} | 99e-001 | -99e+003 |
; | Group Separator | {0:#,##0.00;(#,##0.00);Zero} | 9.88 | (98,765.00) |
Dates - Standard Formatting
The following standard specifiers are available for formatting dates:
Specifier | Name | Syntax | Output (using June 6th, 2000, at 00:00:01) |
---|---|---|---|
d | Short Date | {0:d} | 01/06/2000 |
D | Long Date | {0:D} | 01 June 2000 |
t | Short Time | {0:t} | 00:00 |
T | Long Time | {0:T} | 00:00:01 |
f | Full date and time (short time) | {0:f} | 01 June 2000 00:00 |
F | Full date and time (long time) | {0:F} | 01 June 2000 00:00:01 |
g | Default date and time (short time) | {0:g} | 01/06/2000 00:00 |
G | Default date and time (long time) | {0:G} | 01/06/2000 00:00:01 |
M | Day and Month | {0:M} | 01 June |
r | RFC1123 date string | {0:r} | Sat, 01 Jun 2000 00:00:01 GMT |
s | Sortable date and time (ISO 8601) | {0:s} | 2000-06-01T00:00:01 |
u | Universal, local time | {0:u} | 2000-06-01 00:00:01Z |
U | Universal, UTC time | {0:U} | 31 May 2000 23:00:01 |
Y or y | Month and Year | {0:Y} | June 2000 |
Dates - Custom Formatting
In addition to the standard date specifiers, the following specifiers can be used for custom date formatting:
Specifier | Name | Syntax | Output (using June 6th, 2000, at 00:00:01) |
---|---|---|---|
dd | Day Number | {0:dd} | 01 |
ddd | Short Day Name | {0:ddd} | Sat |
dddd | Full Day Name | {0:dddd} | Saturday |
f, ff, fff etc… | Second fractions | {0:fff} | 000 |
gg | Era | {0:gg} | A.D. |
hh | 2 digit hour (12 hour) | {0:hh} | 00 |
HH | 2 digit hour (24 hour) | {0:HH} | 00 |
mm | 2 digit minute | {0:mm} | 00 |
MM | Month Number | {0:MM} | 06 |
MMM | Short Month Name | {0:MMM} | Jun |
MMMM | Full Month Name | {0:MMMM} | June |
ss | Seconds | {0:ss} | 01 |
tt | AM/PM | {0:tt} | AM |
yy | 2 digit year | {0:yy} | 00 |
yyyy | 4 digit year | {0:yyyy} | 2000 |
zz | Short Timezone Offset | {0:zz} | +01 |
zzz | Full Timezone Offset | {0:zzz} | +01:00 |
: | Separator | {0:hh:mm:ss} | 00:00:01 |
/ | Separator | {0:dd/MM/yyyy} | 01/06/2000 |
All of these specifiers are culture-dependent (apart from the Universal specifiers, which are universal and therefore not culture-dependent), and will depend on the Locale of the current thread - the example outputs come from my box running with the “en-UK” locale, but would be different when run on another locale.
Enumerators
The following standard specifiers are available for formatting Enums:
Specifier | Name | Syntax | Output (using DateTimeKind.Local) |
---|---|---|---|
g | General (Flag if present, Decimal otherwise) | {0:g} | Local |
f | Flag (string) | {0:f} | Local |
d | Decimal | {0:d} | 2 |
x | Hexadecimal | {0:z} | 00000002 |
Strings
The only formatting available to Strings is the padding option, which can be positive or negative depending on whether the string should be left- or right-aligned:
String.Format("'{0,10}'", "John");
-> ' John'
String.Format("'{0,-10}'", "John");
-> 'John '
Culture
As mentionned previously, the currency and date formats are culture-dependent. This means that the output of the formatted string will depend on what locale is made available to the method. By default, the String.Format method will use the locale of the current thread as a basis for formatting; the current thread locale can be set as such:
//set the culture in the current thread to French Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); String.Format("{0:c}", 22.45);
-> 22,45 €
String.Format is also overloaded to accept a CultureInfo argument, which can be used to format a DateTime or currency based on a specific locale:
//Format a currency using French currency formatting String.Format("{0:c}", 22.45, new CultureInfo("fr-FR"));
-> 22,45 €
Sample Code
Sample code demonstrating all of the specifiers mentionned above and setting the culture for the current thread:
C# String Formatting - Sample Code
This code can be compiled using the C# compiler and run from the command line. Using the command prompt (and assuming the code has been saved to C:\StringFormatExample.cs):
C:>SET PATH=%PATH%;%WINDIR%\Microsoft.NET\Framework\v2.0.50727; C:>csc StringFormatExample.cs Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42 for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727 Copyright (C) Microsoft Corporation 2001-2005. All rights reserved. C:>StringFormatExample.exe fr-FR
Reference
There is actually quite a reasonable amount of reference material available - I only found the other two blog posts after writing this though :-/ (I decided to post anyways)
MSDN docs for standard number formatting
MSDN docs for custom number formatting
MSDN docs for standard date formatting
MSDN docs for custom date formatting
https://blog.stevex.net/index.php/string-formatting-in-csharp/
https://idunno.org/archive/2004/14/01/122.aspx