Interpolated string formatting issue - c#

I have stumbled upon one issue with interpolated strings for a several times now.
Consider the following case:
double number = 123.4567;
var str = $"{{{number:F2}}}"; //I want to get "{123.45}"
Console.WriteLine(str); // Will print "{F2}"
A little surprising at first but once you realize how the curly brackets are paired it makes sense. Two following curly brackets are an escape sequence for a single curly in the interpolated string. So the opening bracket of the interpolated expression is paired with the last curly in the string.
___pair____
| |
$"{{{number:F2}}}";
Now you could do the following to break the escape sequence:
var str = $"{{{number:F2} }}"; // This will be "{123.45 }"
Notice the space character this approach adds to the output. (Not ideal)
My question:
Lets say I want to use a single interpolated string to get exactly the output "{123.45}"
Is this at all possible without doing something hackish like the following?
var s = $"{{{number:F2}{'}'}";

This is an expected behavior of string interpolation. It is mentioned at this Microsoft document. The below content is from Microsoft link only.
Opening and closing braces are interpreted as starting and ending a format item. Consequently, you must use an escape sequence to display a literal opening brace or closing brace. Specify two opening braces ("{{") in the fixed text to display one opening brace ("{"), or two closing braces ("}}") to display one closing brace ("}"). Braces in a format item are interpreted sequentially in the order they are encountered. Interpreting nested braces is not supported.
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:
The first two opening braces ("{{") are escaped and yield one
opening brace.
The next three characters ("{0:") are interpreted as the start of a
format item.
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}".
The last brace ("}") is interpreted as the end of the format item.
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}

Assuming that it is not required to use a named format string, you can use:
var s = $"{{{number:#.#0}}}";

Related

How do I escape a brace in an interpolated string, after a brace with a format string?

The following crashes:
$"{{{DayOfWeek.Friday:d}}}"
If I put a space after the first closing brace, it works, but I don't want a space there.
So 2 questions:
Why does it crash, instead of treating the last 2 braces as a literal brace?
How can I do what I am trying to do?
Please have a look at Escaping Braces documentation
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: The first two opening braces
("{{") are escaped and yield one opening brace. The next three
characters ("{0:") are interpreted as the start of a format item. 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}".
This is exactly your case, you are getting incorrect format specifier as a result.
For you code you can try to use the old string.Format
string.Format("{0}{1:d}{2}", "{", DayOfWeek.Friday, "}");

String.Format: Hexadecimal ("X") Format Specifier and Zero (0) [duplicate]

This question already has answers here:
How to use string.Format() to format a hex number surrounded by curly brackets?
(4 answers)
Closed 5 years ago.
Now, here is a strange problem I have with String.Format while formatting zero as a hex string. Please take a look at the following example:
public override string ToString()
{
return $"{{{LowPart:X}-{HighPart.ToString("X")}}}";
}
Above code works alright with HighPart being zero, but the following two give me a wrong result:
public override string ToString()
{
return $"{{{LowPart:X}-{HighPart:X}}}";
}
public override string ToString()
{
return string.Format("{{{0:X}-{1:X}}}", LowPart, HighPart);
}
With LowPart being 50 for example and HighPart being 0, both of these will return "{32-X}" instead of "{32-0}".
I don't know the "Why" that this happens. I also tried googling it and found no answer. So here I am to see if anyone have a clue about this.
BTW, I am using VS2015 and .Net4.5
Edit: Turns out that the problem is with the ending }} in the string. Still strange to me though.
Seems like a bug in formatting engine as if I add a space there in the middle, it will work. Like: $"{{{LowPart:X}-{HighPart:X} }}"
Console.WriteLine(string.Format("{0:X}", 50));
returns 32. 50 in decimal is 32 in hexadecimal, because 2*16^0+3*16^1 = 2 + 48 = 50
Edit after understanding what the question is about:
I believe this page holds the answer (https://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx)
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:
The first two opening braces ("{{") are escaped and yield one opening
brace.
The next three characters ("{0:") are interpreted as the start of a
format item.
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}".
The last brace ("}") is interpreted as the end of the format item.
The final result that is displayed is the literal string, "{D}". The
numeric value that was to be formatted is not displayed.
So the "{1:X}}}" is interpreted as "X}", because it tries to apply "X}" which is not recognized. The same happens in String.Format("{0:HELLO}",50)
The credit for explaining the issue goes to Hans Kesting's answer in How to use string.Format() to format a hex number surrounded by curly brackets? I flagged the question as possible duplicate because of this.

Escape Character Associativity C# 6 String Interpolation

Given:
double price = 5.05;
Console.Write($"{{Price = {price:C}}}");
and the desired output: {Price = $5.05}
Is there any way to associate the last two curly braces as an escaped '}' so the interpolation works as intended? As it stands, the first two are escaped(I assume?), and the output is :{Price = C}
Console.Write($"{{Price = {price:C} }}");
works as expected, but with the extra space. And I can concatenate the tail brace, which I consider a poor man's solution. Is there a colloquial rich man's solution? Thanks.
This arises because of an "oddity" in the behavior of string.Format, and our desire to have a precise 1-to-1 mapping between interpolations and inserts in the generated format string. In short, the language behavior precisely models the behavior of string.Format.
In an interpolation (the thing inside the curly braces), the expression ends either at a colon (which starts a format string), or a close curly brace. In the latter case a doubled curly brace has no special meaning because it isn't inside a literal part of the string. So three curly braces in a row would be interpreted as a close to the interpolation, followed by a literal (escaped by doubling) close curly brace. But after the colon the format string is given for that interpolation, and that format string is any string, and it is terminated by a close curly brace. If you want a close curly brace inside your format string, you simply double it up. Which is what you have unintentionally done.
CoolBots gave the best way of handling this https://stackoverflow.com/a/42993667/241658
Read the "Escaping Braces" section of https://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx for an explanation of precisely this issue.
Curious workaround:
var p = price.ToString("C");
Console.Write($"{{Price = {p}}}");
For some reason, $"{{Price = {p}}}" and $"{{Price = {p:C}}}" have different associativity outcomes, which feels like a compiler bug. I'll ask around! Note that it is consistent with how string.Format applies the same rule, so it might be intentionally propagating an earlier framework oddity.
You can interpolate instead of concatenate - pass it as a string literal:
double price = 5.05;
Console.Write($"{{Price = {price:C}{"}"}");
Well you can try with less used escape characters. Maybe \b will work as it doesn't print anything and it had no function for a really long time. Something like:
double price = 5.05;
Console.Write($"{{Price = {price:C}\b}}");
If that doesn't work for you, you can try with special UNICODE characters like U+200B or U+FEFF:
double price = 5.05;
Console.Write($"{{Price = {price:C}\x8203}}");
Escape characters: https://blogs.msdn.microsoft.com/csharpfaq/2004/03/12/what-character-escape-sequences-are-available/
UNICODE space characters: https://www.cs.tut.fi/~jkorpela/chars/spaces.html
When there are some problems with C# 6 syntax why not to use traditional string.Format() instead?
double price = 5.05;
Console.WriteLine(string.Format("{{Price = {0}}}", price.ToString("C")));

On C# escape curly braces and a backslash after

I am trying to format a text so I can provide a template some RFT text.
My string is declared with the stringformater as:
var fullTitleString = string.Format(
CultureInfo.CurrentCulture,
"{{\\Test",
title,
filterName);
But I keep obtaining a string as "{\Test". Using a single backslash results on errors at it does not understand the \T escaped character.
Doing #"{{\Test" also yields "{\Test". I have looked over the MSDN documentation and other questions where they tell to use another backslash as escaping character, but it doesn't seem to work.
There are two levevls of escaping here:
1. Escaping string literals
In c# strings, a backslash (\) is used as special character and needs to be escaped by another \. So if your resulting string should look like \\uncpath\folder your string literal should be var s = "\\\\uncpath\\folder".
2. Escape format strings
string.Format uses curly braces for place holders, so you need to escape them with extra braces.
So let's say you have
string title = "myTitle";
string filterName = "filter";
then
string.Format("{{\\Test {0}, {1}}}", title, filterName);
results in
{\Test myTitle, filter}
If you want two curly braces at the beginning, you need to put four in your format string:
string.Format("{{{{\\Test {0}, {1}}}", title, filterName);
results in
{{\Test myTitle, filter}
If you provide a clear example of what you are trying to achieve, I may tell you the correct format string.
Side note: In C# 6 the last example could also be $"{{{{\\Test {title}, {filterName}}}" (using string interpolation without explicitly calling string.Format)
NOTE: The Visual Studio debugger always shows the unescaped string literal. So if you declare a string like string s = "\\" you will see both backslashes in your debugger windows, but if you Console.WriteLine(s) only one backslash will be written to console.

Using "#" to insert multiple lines of strings in StringBuilder

I have a StringBuilder object and wanted to used its Append() method to add this whole string to it:
so I used "#" and copy pasted that whole string like this, but it gives a lot of errors such as "; expected ", "Invalid Expression '<'" , etc
myString.Append(#"COPY-PASTED-THAT_WHOLE-STRING");
What is the correct way of adding this string to my string builder object?
Thank you.
Even with an # prefixing the string, you need to escape any " characters, otherwise they will be interpreted as the end of the string literal.
EDIT:
e.g.
var entity = #"<!ENTITY xsd ""http://www.w3.org/2001/XMLSchema#"">";
Double-quotes (") inside the string you want to paste need to be escaped by being replaced with two consecutive double-quotes, as in "". Here's a trick to use:
Paste your string into a new instance of Notepad
Replace all double quotes (") with two double quotes ("")
Select and copy the content from Notepad back into clipboard
Paste it into #"…" in your code/text editor
From C# docs:
In a verbatim string literal, the characters between the delimiters
are interpreted verbatim, the only exception being a
quote-escape-sequence.
You can use the # syntax to add multiple lines. But you need to escape the "s inside your string by using ""
For example
#"<Ontology xmlns=""http://www.w3.org/2002/07/owl#"""
If you don't escape them, C# will treat the quote mark as the end of the string.
One option, as others have said, is to escape all of the double quotes (") with a double double quote ("").
What I prefer to do, as it makes the code more readable, when adding an XML block as a literal string, is to use single quotes rather than double quotes. Just put the XML file into a text editor and do a replace all on double quote with a single quote (').
Another option, since your XML literal isn't all that short, is to put it into a file and read in that file at runtime.
You can escape them like this as well...
#"<Ontology xmlns=\"http://www.w3.org/2002/07/owl#\""

Categories