27

I want to output a formatted number inside braces (example {$100.00}) using a string.Format(fmt, x) statement with x=100.

{ 
    var x = 100M;
    // works fine without a format specifier
    string.Format("{{{0}}}", x);    // "{100.00}"

    // fails with a format specifier
    string.Format("{{{0:C}}}", x);  // "{C}"

    // works with spaces
    string.Format("{{ {0:C} }}", x);  // "{ $100.00 }"
}

So what format string should I use above to get {$100} without using a clunky solution such as string.Format("{0}{1:C}{2}", "{", x, "}");

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • 7
    Interestingly, `string.Format("{{{0:C}}}", x);` does return `"{$100.00}"` on Mono. MS.NET bug? O_o – cdhowie Aug 14 '14 at 15:51
  • 1
    Really you would think `string.Format()` bug would have been ironed out since `.NET 1.1`. It is has been there the entire lifetime of the framework. – John Alexiou Aug 14 '14 at 15:54
  • This may be related: http://stackoverflow.com/a/15085178/1218281 (The design bug part) – Cyral Aug 14 '14 at 15:55
  • @SergeyBerezovskiy But it's not a compiler bug at all. – cdhowie Aug 14 '14 at 15:55
  • 2
    @SergeyBerezovskiy This isn't behavior of the compiler, it's the behavior of a particular .NET library, and thus would affect every single .NET language identically. – Servy Aug 14 '14 at 15:56
  • @SergeyBerezovskiy Yes. – cdhowie Aug 14 '14 at 15:56
  • 1
    @SergeyBerezovskiy It's a string literal. Are you asserting that the C# compiler is going to change the string literal into something different, and that's why the code doesn't work, rather than there being a problem in the implementation of `string.Format`? – Servy Aug 14 '14 at 15:57
  • There is a [blog post](http://blogs.msdn.com/b/brada/archive/2003/12/26/50978.aspx) with information on this as well. – Cyral Aug 14 '14 at 15:58
  • Interestingly this half works, `string.Format("{{{0:C} }}", x);` produces `{$100.00 }` – John Alexiou Aug 14 '14 at 16:00
  • 3
    I guess since this is a documented gotcha with the formatting syntax that Mono doing the "right thing" is actually a bug. I have [filed a bug](https://bugzilla.xamarin.com/show_bug.cgi?id=22114) against corlib in Mono. – cdhowie Aug 14 '14 at 16:27

3 Answers3

34

This is actually documented on MSDN under Composite Formatting:

The way escaped braces are interpreted can lead to unexpected results. For example, consider the format item "{{{0:D}}}", which is intended to display an opening brace, a numeric value formatted as a decimal number, and a closing brace. However, the format item is actually interpreted in the following manner:

  1. The first two opening braces ("{{") are escaped and yield one opening brace.
  2. The next three characters ("{0:") are interpreted as the start of a format item.
  3. The next character ("D") would be interpreted as the Decimal standard numeric format specifier, but the next two escaped braces ("}}") yield a single brace. Because the resulting string ("D}") is not a standard numeric format specifier, the resulting string is interpreted as a custom format string that means display the literal string "D}".
  4. The last brace ("}") is interpreted as the end of the format item.
  5. The final result that is displayed is the literal string, "{D}". The numeric value that was to be formatted is not displayed.

One way to write your code to avoid misinterpreting escaped braces and format items is to format the braces and format item separately. That is, in the first format operation display a literal opening brace, in the next operation display the result of the format item, then in the final operation display a literal closing brace. The following example illustrates this approach.

int value = 6324;
string output = string.Format("{0}{1:D}{2}", 
                             "{", value, "}");
Console.WriteLine(output);
// The example displays the following output: 
//       {6324}

(note that I add the code only because it's referenced in the MSDN article - not to suggest it as a solution.)

If you're not concerned about culture differences you could use "{{{0:$#,##0.00}}}" as a format string.

Community
  • 1
  • 1
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • 5
    There is a [blog post](http://blogs.msdn.com/b/brada/archive/2003/12/26/50978.aspx) with information on this as well. – Cyral Aug 14 '14 at 15:58
  • And a code project article at http://www.codeproject.com/Articles/371232/Escaping-in-Csharp-characters-strings-string-forma – Greg Bray Aug 14 '14 at 20:09
  • 1
    Was there any need to escape closing braces? If one wanted symmetry, I could understand having something like `{[}` as a code for an open-brace character and `{]}` as a code for a closing brace (note that neither will unbalance the open-close brace count of the string), but would there have been any problem with not having close-braces treated as special except after a recognized open brace? – supercat Aug 14 '14 at 20:27
  • @supercat: If you were to tokenize one of these strings, you'd be happier if you knew that a lone `}` closes a format item rather than having to worry about whether you've seen an unmatched lone `{` yet. The latter is actually the parser's job; a proper tokenizer doesn't care about sentence-level details. – cHao Aug 16 '14 at 19:19
  • @cHao: At present, a `}` only closes a format string if it's unmatched, but the definition of "unmatched" means "not preceded by an odd number of `}` characters". I would suggest that it would be better for "unmatched" to mean "not preceded by either `{[` or `{]`", without regard for any characters preceding those, though I guess for compatibility with code that uses `{{` as a left-bracket escape it would have to count whether the `{[` sequence was preceded by an odd number of left-bracket characters. Personally, I've never liked doubled-character escapes, since... – supercat Aug 17 '14 at 19:08
  • ...it means that there's no limit to the number of characters that need to be examined from the right to know what something means. If the only legal escapes were `{[}` and `{]}` then any close-brace which formed part of one of those sequences would not end a tag; any other close brace would end a tag. – supercat Aug 17 '14 at 19:09
  • @supercat: Your question was, "...would there have been any problem with not having close-braces treated as special except after a recognized open brace?" And the answer is yes -- it makes things more complicated. If a single brace is always special, and a double brace is always an escape, then the string can be interpreted from left to right with no more than one char of lookahead. If a lone `}` has two meanings, on the other hand, then you have additional context info to check each time you see one. – cHao Aug 18 '14 at 01:47
  • @supercat: As for "unmatched", you're using it differently than i was. What i meant by "an unmatched lone `{`" was basically a `{` that doesn't already have a corresponding `}`. Where you would have used "unmatched", i was using "lone". – cHao Aug 18 '14 at 01:56
14

Try to use this code:

string.Format("{{{0}}}", x.ToString("C"))
Norfolc
  • 458
  • 3
  • 10
  • 6
    @Cyral Not necessarily. The OP may be using some control that has a "Format" property that he/she is trying to set. Using `ToString` may just be a way to illustrate the problem without referencing the actual source directly. So adding `ToString` onto the value may not be a viable option. – D Stanley Aug 14 '14 at 16:03
4

Depending on the application you may be able to use a zero-width space to trick the string.Format method into doing what you want. See Spaces in Unicode for a list of options, but \x200B will add a zero-width space to a C# string, so you could try using:

string.Format("Pi is {{{0:N2}\x200B}} when rounded", Math.PI)

which should result in Pi is {3.14​} when rounded when displayed in an application that supports unicode. If the application doesn't support unicode it may end up looking like Pi is {3.14​} when rounded

Greg Bray
  • 14,929
  • 12
  • 80
  • 104
  • That just looks ugly compared to other options listed. Strikes me as "magic number" voodoo. `string.Format("{0}{1:D}{2}", "{", value, "}");` is ugly as well, but at least it's easy to decipher what it is. ` – WernerCD Aug 15 '14 at 03:43
  • And I could do also `string.Format("{{{0:C} }}",x).Replace(" ",String.Empty);` but no. – John Alexiou Aug 15 '14 at 17:14