다음을 통해 공유


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

  • Anonymous
    January 16, 2004
    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}}}"
  • Anonymous
    January 17, 2004
    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 :)
  • Anonymous
    February 03, 2004
    For those who seek:

    Solution to the last challenge :
    String.Format("{0}{1:N}{2}", "{",i,"}")

  • Anonymous
    February 13, 2004
    Another solution would be this:

    string s = String.Format("{{{0}}}", i.ToString("N"));
  • Anonymous
    October 06, 2006
    When working on .NET projects, we often need to find the right API or the proper parameters to pass in