Unintelligible

Thursday, October 5, 2006

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 substitution
  • padding 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

posted by Nick at 6:51 pm - filed in .net