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.

Comments (7)

  1. Mike Dunn says:

    Just curious, was there a reason you didn’t use an easier-to-parse syntax? I personally dislike the "double the char to escape it" system because I find it harder to read and it leads to hard-to-grok parsing situations like the ones you described.

    I like how Perl regexs work, if you want to escape a special character, you just put a backslash in front of it. /./ means "match any non-newline char" while /./ means "match a period"

    I would much rather use "{{0}}" instead of "{{{0}}}"

  2. foo says:

    Mike,

    What you propose as "{{0}}" String.Format would actually see as "{{0}}". What you’d really need to pass in is "\{{0}\}", so that it sees the \ you entered as a single . I imagine this may be easier to enter in VB or maybe using C#’s @"" string format.

    Even your way it’s not easy to get it right 🙂

  3. Jocke Bocke says:

    For those who seek:

    Solution to the last challenge :

    String.Format("{0}{1:N}{2}", "{",i,"}")

  4. David Stucki says:

    Another solution would be this:

    string s = String.Format("{{{0}}}", i.ToString("N"));

  5. When working on .NET projects, we often need to find the right API or the proper parameters to pass in