String Formatting FAQ

Largly modivated by your comments on a recent post, Kit George recently posted a FAQ on string formatting on the BCL Website

A couple of interesting items below... or suggest your own.

 

How do I write out a curly bracket in string formats?
In order to print out a curly bracket in a string using string formatting, simply put two of the curly brackets in a row. This is referred to as 'escaping' the curly bracket.

[C#]string s = String.Format("{{ hello to all }}");Console.WriteLine(s); //prints '{ hello to all }'

Do escaped curly brackets have any odd behaviors I need to be aware of?
There is an interesting result from the decision to use two curly brackets in order to print a single curly bracket in a format string.
If you want to actually use the standard ability to use a referenced parameter in string formatting, you might write this kind of code:

[C#]int i = 42;string s = String.Format("{0}", i); //prints '42'

However, what if I want to print out '{42}'? It would seem that this line of code is fairly intuitive, based on my attempt to escape the curly brackets:

[C#]int i = 42;string s = String.Format("{{{0}}}", i); //prints '{42}'

Now however, I want to take advantage of some of the more robust formatting options available, and specify a format for the variable. I want to print it out in Number format, using the N specifier:

[C#]int i = 42;string s = String.Format("{0:N}", i); //prints '42.00'

Going a step further, I want to print out my value with curly brackets around it, using the Number format specifier. So, I expected this to work:

[C#]int i = 42;string s = String.Format("{{{0:N}}}", i); //prints '{N}'

The question is, why did this last attempt fail? There's two things you need to know in order to understand this result:

  1. When providing a format specifier, string formatting takes these steps:
    • Determine if the specifier is longer than a single character: if so, then assume that the specifier is a custom format. A custom format will use suitable replacements for your format, but if it doesn't know what to do with some character, it will simply write it out as a literalliterals found in the format
    • Determine if the single character specifier is a supported specifier (such as 'N' for number formatting). If it is, then format appropriately. If not, throw an ArgumnetException
  2. When attempting to determine whether a curly bracket should be escaped, the curly brackets are simply treated in the order they are received. Therefore, "{{{" will escape the first two characters and print the literal '{', and the the third curly bracket will begin the formatting section. On this basis, in "}}}" the first two curly brackets will be escaped, therefore a literal '}' will be written to the format string, and then the last curly bracket will be assumed to be ending a formatting section

With this information, we now can figure out what's occurring in our "{{{0:N}}}" situation. The first two curly brackets are escaped, and then we have a formatting section. However, we then also escape the closing curly bracket, before closing the formatting section. Therefore, our formatting section is actually interpreted as containing "0:N}".

Now, the formatter looks at the format specifier and it sees "N}" for the specifier. It therefore interprets this as a custom format, and since neither N or } mean anything for a custom numeric format, these characters are simply written out, rather than the value of the variable referenced.

This last bit gets a little confusing, but it becomes clearer if we compare this behavior with something like the following:

[C#]int i = 42;string s = String.Format("{0:N!}", i); //prints 'N!'

The same thing occurs in this situation, but it's a little clearer, because we don't have any escaping. Basically, the format specifier is seen as "N!", which gets interpreted as a custom format (longer than one character), and because the custom format has nothing special to do with numbers, then N! is simply used for the value.