.NET provides you with ways to format the output of strings. You can use the String.Format to format your strings using different format specifiers.

string str1 = "This";
string str2 = "That";
string str3 = String.Format("{0} and {1}", "This", "That");

This and That

The String.Format method accepts the string containing the format specifiers ({0},{1} and so on), and the following arguments are the strings that will be formatted by those format specifiers. The compiler looks at the numbers in the format specifier, takes the argument with the same index in the argument list, and makes the substitution. The format specifiers automatically convert the data into string. Right now, there is no formatting that took place. We will save that for later.

You might notice that String.Format is quite like the Console.WriteLine method. In fact, Console.WriteLine uses the same technique described above.

Since { and } indicates the start and end of a format specifier, what if you want to print these characters?

Console.WriteLine("{{{0}}}", 7);

To print an { or }, you simply put two of each. In the code above, between the pairs of those braces lies the format specifier {0} that’s why it was replaced by 7 in the output.

It is important to understand that the argument associated with a format specifier is determined by the argument number, not the argument’s position in the argument list. This means the same argument can be output more than once within the same call to WriteLine(). It also means that arguments can be displayed in a sequence differently than they are specified in the argument list. For example, consider the following program:

Console.WriteLine("{0} {0} {0} {1} {1}", "hello", "world");
hello hello hello world world

Numeric Format Specifiers

You can use a list of numeric format specifiers to format numerical data:

Console.WriteLine("{0:C}", 500);

You can see that the integer 500 was formatted into a currency using the C format specifier.

You can also use precision specifiers which are numbers that modify the result of the formatting. For example, if you want 3 decimal places for a number, you do the following:

Console.WriteLine("{0:F3}", 100); // 500.000

A list of numeric format specifiers is listed below.

Specifier Format Meaning of Precision Specifier
C Currency Specifies the number of decimal places.
c Same as C
D Whole number numeric data. (Use with integers only.) Minimum number of digits. Leading zeros will be used to pad the result, if necessary.
d Same as D.
E Scientific notation (uses uppercase E) Specifies the number of decimal places. The default is six.
e Scientific notation (uses lowercase e) Same as E
F Fixed point notation. Specifies the number of decimal places.
f Same as F.
G Use either E or F format, whichever is shorter. See E and F.
g Use either e or f format, whichever is shorter. See e and f.
N Fixed-point notation, with comma separators. Specifies the number of decimal places.
n Same as N.
P Percentage Specifies the number of decimal places.
p Same as P.
R Numeric value that can be parsed, using Parse(), back into its equivalent internal form. (This is called the “round-trip” format.) Not used.
r Same as R.
X Hexadecimal (uses uppercase letters A through F) Minimum number of digits. Leading zeros will be used to pad the result, if necessary.
x Hexadecimal (uses lowercase letters a through f) Minimum number of digits. Leading zeros will be used to pad the result, if necessary.

The precise effect of certain format specifiers depends upon the cultural settings. For example, the currency specifier, C, automatically displays a value in the monetary format of the selected culture. For most users, the default cultural information matches their locale and language. Thus, the same format specifier can be used without concern about the cultural context in which the program is executed.

Here is a program that demonstrates several of the numeric format specifiers:

double v = 17688.65849;
double v2 = 0.15;
int x = 21;

Console.WriteLine("{0:F2}", v); // 17688.66
Console.WriteLine("{0:N5}", v); // 17, 688.65849
Console.WriteLine("{0:e}", v);  // 1.768866e+004
Console.WriteLine("{0:r}", v);  // 17688.65849
Console.WriteLine("{0:p}", v2); // 15.00 %
Console.WriteLine("{0:X}", x);  // 15
Console.WriteLine("{0:D12}", x);  // 000000000021
Console.WriteLine("{0:C}", 189.99); // $189.99

Notice the effect of the precision specifier in several of the formats. Also, note that you can use the ToString() method and pass the numeric format specifier.

int x = 500;
Console.WriteLine(x.ToString("C")); // $500.00

Padding with Format Specifiers

You can also used the format specifiers to add padding to the string. The following demonstrates just that:

Console.WriteLine("{0,10}", "hello"); 
Console.WriteLine("{0,-10}{1}", "hello", "world");
hello     world

You use the following syntax {x:y} where x is the argument number, and y is the length of the padding. A positive length adds the padding to the left while a negative length adds the padding to the right. The amount of padding is determined by the length of the string which will be substituted. For example, if the length of the string is 5 and the length of the padding is 10, then the actual padding that will be displayed is 5 spaces. If the length of the padding is less that the length of the string, then nothing will happen. You can even combine them with numeric format specifiers such as the following:

Console.WriteLine("{0,10:C}", 500);

Note that the padding depends on the length of the formatted so even though 500 has a length of 3 and the final padding is supposed to be 7, the padding was reduced to 4 because of the $ sign the decimal point the two zeroes that has been added by the numeric format specifier.

Custom Numeric Format

Custom numeric format specifiers allow you to indicate an example of how you will represent your output. You will be using different special characters which function as placeholders. The following table presents the available custom format placeholder characters.

Placeholder Meaning
# Digit
. Decimal point
, Thousands separator
% Percentage, which is the value being formatted multiplied by 100.
0 Pads with leading and trailing zeros
; Seperates sections that describe the format for positive, negative, and zero value.
E0 E+0 E-0
e0 e+0 e-0
Scientific notation.

The period specifies the position of the decimal point. The # placeholder can hold a digit and can occur on the left or right side of the decimal point, or even by itself. If the # appears in the right side of the decimal point, it specifies the precision of the number of decimal digits. The value will be rounded if it is necessary. If the # is found in the left of the decimal point, it specifies the digit positions for the whole-number part of the value and leading zeroes will be added if deemed necessary. If the actual number has more digits the number of #’s to the left of the decimal point, all the digits will be displayed. For example, if the number is 12345 and you only use ### which can only hold 3 digits, then all the five digits will still be displayed. If the original value has a decimal part and you use no decimal portion in your custom numeric format specifier, the value will be rounded to a whole number. For example, if the original value is 123.45 and you used ###, then it will be rounded down to 123. Zeroes are not significant such as a trailing zero, therefore, they will not be displayed. This causes a somewhat odd effect because a format such as ##.## displays nothing at all if the value being formatted is 0. To display a zero, you use the 0 placeholder instead as seen in the table above. You use the 0 placeholder to add leading or trailing zeroes. It can be placed on both left or right of the decimal point.

Console.WriteLine("{0:00##.#00}", 21.3); // 0021.300

You can place commas into large numbers to indicate the thousands position. For example:

Console.WriteLine("{0:#,###.#}", 3421.3); // 3,421.3

You don’t need to specify a comma for each position. Commas will automatically inserted for every third digit from the left of the decimal point. For example:

Console.WriteLine("{0:#,###.#}", 8763421.3); // 8,763,421.3

Commas have another meaning. When they occur on the immediate left of the decimal point, they act as a scaling factor. Each comma causes the value to be divided by 1,000. For example:

Console.WriteLine("{0:#,###,.#}", 8763421.3); // 8,763.4

As the output shows, the value is scaled in terms of thousands. In addition to the placed holders, a custom format specifier can contain other characters. Any other characters are simply passed through, appearing in the formatted string exactly as they appear in the format specifier. For example, this WriteLine() statement:

Console.WriteLine("Fuel efficiency is {0:##.# mpg}", 21.3); // 21.3 mpg

You can also use the escape sequences, such as   or 
, if necessary.

The E and e placeholders cause a value to be displayed in scientific notation. At least one 0, but possibly more, must follow E or e. The 0’s indicate the number of decimal digits that will be displayed. The decimal component will be rounded to fit the format. Using an uppercase E causes an uppercase E to be displayed; using a lowercase e causes a lowercase e to be displayed. To ensure that a sign character precedes the exponent, use the E+ or e+ forms. To display a sign character for negative values only, use E,e,E-,e-.

The “;” is a seperator which enables you to indicate different formats for positive, negative, and zero values. Here is the general form of a custom specifier that uses the “;“: positive-fmt;negative-fmt;zero-fmt. For example:

Console.WriteLine("{0:#.##;(#.##);0.00}", num);

If num is positive, the value that will be displayed has two decimal places otherwise, if it is negative, the value is displayed with two decimal places and is inside a set of perentheses. If num is zero, 0.00 will be displayed. When you use seperators, there is no need to supply all parts. If you just want to specify how positive and negative values will look, omit the zero format. To use the default for negative values, omit the negative format. In this case, the positive format and the zero format will be seperated by two semicolons.

The following example demonstrates just a few of the many possible custom formats that you can create:

double num = 64354.2345;

Console.WriteLine("{0:#.##}", num); // 64354.23
Console.WriteLine("{0:#,###.##}", num); // 64,354.23
Console.WriteLine("{0:#.###e+00}", num); // 6.435e+04
Console.WriteLine("{0:#0,}", num); // 64
Console.Writeline("{0:#.#;(#.##);0.00}", num); // 64354.2
num = -num;
Console.WriteLine("{0:#.#;(#.##);0.00}", num); // (64354.23)
num = 0.0;
Console.WriteLine("{0:#.#;(#.##);0.00}", num); // 0.00
num = 0.95;
Console.WriteLine("{0:#%}", num); // 17%