C# & .NET (C# 14, .NET 10 LTS) Module 4 – Strings and text
Interpolation, raw strings and UTF-8 literals
Format values inside C# strings with interpolation, widths and format strings, write JSON with raw string literals, and get UTF-8 bytes from u8 literals.
What you will learn
- Format values inline with interpolation holes, widths and format strings
- Write multi-line text and JSON templates with raw string literals and $$ holes
- Choose a regular, verbatim, raw or UTF-8 literal for a piece of text
- Predict which culture an interpolated string uses for its numbers
Before you start
What you’ll learn · Before you start
What you will learn
- Format values inline with interpolation holes, widths and format strings
- Write multi-line text and JSON templates with raw string literals and $$ holes
- Choose a regular, verbatim, raw or UTF-8 literal for a piece of text
- Predict which culture an interpolated string uses for its numbers
Before you start
On this page
A bill line such as Paneer wrap 1 189.50 takes one interpolated string, $"{item,-12}{qty,4}{price,10:N2}",
and no padding loops. C# also has four kinds of string literal, and raw strings turn a JSON template full of quotes
and braces from a wall of backslashes into text you can read.
You will format values with interpolation, widths and format strings, write multi-line text and JSON as raw strings, and get bytes from UTF-8 literals without encoding anything at run time.
Interpolation puts formatted values into a string
An interpolated string: A string literal that starts with $ and holds holes in braces, such as $"{qty,4}": each hole is an expression, with an optional width and format string, whose formatted value replaces it in the text. starts with $, and each pair of braces in it is a hole
with up to three parts: {expression,width:format}. The width pads the value to at least that many chars, on the
left when it is positive and on the right when it is negative, and the format string is the value’s own, such as
N2 for a number with two decimals or P1 for a percentage:
using System.Globalization;
// The culture decides the separators of N2 and P1. This line fixes it, so the
// program prints the same on every computer (the next section changes it).
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
(string Item, int Qty, decimal Price)[] lines =
[
("Masala tea", 2, 45m),
("Paneer wrap", 1, 189.5m),
("Water, 1 L", 3, 20m),
];
Console.WriteLine($"{"Item",-12}{"Qty",4}{"Price",10}{"Total",10}");
decimal sum = 0;
foreach (var (item, qty, price) in lines)
{
decimal total = qty * price;
sum += total;
Console.WriteLine($"{item,-12}{qty,4}{price,10:N2}{total,10:N2}");
}
Console.WriteLine($"{"Sum",-26}{sum,10:N2}");
Console.WriteLine($"{"Tea, share of the sum",-26}{lines[0].Qty * lines[0].Price / sum,10:P1}");
// Alignment pads a value to a width; it never cuts one that is longer.
Console.WriteLine($"[{"ab",5}] [{"ab",-5}] [{"abcdefg",5}]"); Output
Item Qty Price Total Masala tea 2 45.00 90.00 Paneer wrap 1 189.50 189.50 Water, 1 L 3 20.00 60.00 Sum 339.50 Tea, share of the sum 26.5 % [ ab] [ab ] [abcdefg]
Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run bill.cs
Step through the price hole of the paneer wrap, from the value to the ten chars it adds:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- Evaluate. The part before the comma is any C# expression. Here it is the variable price, a decimal that holds 189.5.
- Format. The part after the colon is a format string for the value's type. N2 means a number with group separators and exactly 2 decimals.
- Culture. The separators come from the current culture. bill.cs set it to the invariant culture on its first line, so the decimal point is a dot and thousands would be split by commas.
- Pad. The number after the comma is the width: at least 10 chars, padded with spaces on the left because 10 is positive. A negative width pads on the right.
- Append. The 10 chars join the line after the item and the quantity, which is how the prices line up in a column. A longer value would be kept whole, never cut.
Interpolation formats every hole with the current culture, which is why the first line of bill.cs fixes it:
there, P1 printed 26.5 % with a space, as the invariant culture writes percentages. The last line shows that a width only pads:
abcdefg stayed 7 chars wide in a hole of width 5.
Checkpoint
Check: Holes, widths and format strings
Raw string literals keep quotes, backslashes and braces as they are
A regular literal needs \\ for a backslash and \" for a quote. A verbatim literal, @"…", treats backslashes as
plain chars and writes a quote twice. A raw string literal: A string literal delimited by three or more quotes, """…""", that needs no escapes. Over several lines, the white space before its closing quotes is removed from every line. starts and ends with
at least three quotes and needs no escapes at all. Over several lines, the spaces before its closing quotes are taken
off every line, so the text can be indented with the code. With $$, a hole takes two braces, {{order}}, and a
single brace stays text, which is what a JSON template needs:
// The same text written as a regular, a verbatim and a raw string literal.
string escaped = "C:\\data\\new\\report.csv"; // \\ is one backslash
string verbatim = @"C:\data\new\report.csv"; // @: a backslash is just a backslash
Console.WriteLine($"{escaped == verbatim} {verbatim}");
string doubled = @"Asha said ""yes""."; // @: a quote is written twice
string raw = """Asha said "yes"."""; // raw: no escapes at all
Console.WriteLine($"{doubled == raw} {raw}");
// A raw interpolated literal over several lines. $$ makes {{ and }} open and close
// a hole, so the single braces of the JSON stay plain text. The spaces before the
// closing """ are taken off every line.
string order = "A-17";
int qty = 2;
string json = $$"""
{
"order": "{{order}}",
"qty": {{qty}},
"tags": ["new", "paid"]
}
""";
Console.WriteLine(json);
Console.WriteLine($"First char '{json[0]}', {json.Split('\n').Length} lines, ends with '{json[^1]}'"); Output
True C:\data\new\report.csv
True Asha said "yes".
{
"order": "A-17",
"qty": 2,
"tags": ["new", "paid"]
}
First char '{', 5 lines, ends with '}'
Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run literals.cs
Both comparisons printed True: the literals differ, the strings are the same. Raw strings also kept the JSON’s own braces and quotes intact and put its first char at the margin. Choose the literal by what the text holds:
| Literal | Write it as | Escapes | Best for |
|---|---|---|---|
| regular | "Line 1\nLine 2" |
\n, \", \\ |
short text with a few special chars |
| verbatim | @"C:\data\new" |
"" for a quote |
Windows paths |
| raw | """…""" |
none | JSON, XML, SQL, text over several lines |
| UTF-8 | "text"u8 |
as a regular literal | bytes for a network or a file |
Checkpoint
Check: Raw and verbatim string literals
UTF-8 literals are bytes that the compiler encodes
Text bound for a network or a file usually leaves as UTF-8 bytes. A UTF-8 literal: A string literal with the suffix u8, such as "OK"u8: the compiler encodes its text as UTF-8 and the literal is a ReadOnlySpan<byte> of those bytes.,
a string literal with the suffix u8, is those bytes: its type is ReadOnlySpan<byte>, and the compiler stores
the encoded bytes in the program, so nothing is encoded when it runs:
using System.Text;
// A UTF-8 literal is bytes, not a string: the compiler encodes the text.
ReadOnlySpan<byte> literal = "नमस्ते"u8;
byte[] encoded = Encoding.UTF8.GetBytes("नमस्ते");
Console.WriteLine($"\"नमस्ते\" as a string: {"नमस्ते".Length} chars");
Console.WriteLine($"\"नमस्ते\"u8: {literal.Length} bytes");
Console.WriteLine($"Same bytes as GetBytes: {literal.SequenceEqual(encoded)}");
Console.WriteLine($"The first char, न, is 3 bytes: {Convert.ToHexString(literal[..3])}");
ReadOnlySpan<byte> header = "Content-Type: application/json\r\n"u8;
Console.WriteLine($"An ASCII header line: {header.Length} bytes, one a char"); Output
"नमस्ते" as a string: 6 chars "नमस्ते"u8: 18 bytes Same bytes as GetBytes: True The first char, न, is 3 bytes: E0A4A8 An ASCII header line: 32 bytes, one a char
Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run utf8.cs
"नमस्ते" is 6 chars as a string and 18 bytes in UTF-8, the same 18 that Encoding.UTF8.GetBytes produced at run
time; each Devanagari char took 3 bytes and each ASCII char 1. UTF-8 literals cannot hold holes, and they are spans,
so they cannot be stored in a field; call ToArray() when you need to keep the bytes.
Checkpoint
Check: UTF-8 literals
Text for machines: interpolation and the current culture, reproduced Pro: opens with a Pro, Premium or Ultimate pass
An interpolated string formats every number and date with the culture of the computer it runs on, so the same line of code writes different text in different countries. The run below sends one request body from three cultures, breaks it on two of them, and fixes it three ways.
In this part:
- The same JSON body from three computers
- Choosing the fix by what reads the text
2 runnable examples (C#, Python) · about 3 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Raw string literals: the rules the compiler enforces Pro: opens with a Pro, Premium or Ultimate pass
The compiler checks every raw string literal against a few rules about its quotes, its lines and its braces. The builds below break one rule each, with the error each one reports, and the table after them says how many quotes and dollar signs a text needs.
In this part:
- Four builds that fail, and what each error means
- How many quotes and dollar signs to write
5 runnable examples (C#, Python) · about 3 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
What interpolation compiles to, and what it allocates, measured Pro: opens with a Pro, Premium or Ultimate pass
The compiler turns an interpolated string into calls on a handler that writes every part into one buffer. The run below counts the bytes that six ways of making a short cache key allocate, and shows three smaller features of the same literals.
In this part:
- Six ways to build a cache key, counted
- Constant interpolated strings, \e and holes over several lines
1 diagram · 1 runnable example (C#) · about 4 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise: lay out a receipt
Add a total line with the bill's widths
Copy bill.cs to your computer, add a fourth item, and give the sum line a hole of width 10 with the format N2
so that it lines up under the totals. Then delete the first line, run it again with dotnet run bill.cs, and see
whether your computer’s culture changes the separators.
The questions below come back to the bill, the JSON template and the bytes of नमस्ते. The idea you have missed most often is asked about first, and each choice explains itself against those runs:
Practice that adapts to you
Practise this lesson
8 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · When would you use a verbatim string, and when a raw string literal?
Both let you write backslashes without escaping them. A verbatim string, @"C:\data\new", still uses quotes as
delimiters, so a quote inside it is written twice, and it keeps every line break and space exactly as typed. A raw
string literal, """…""", needs no escapes at all: quotes and backslashes are plain text, and over several lines
the spaces before the closing quotes are removed from every line, so the text can follow the code’s indentation.
Use a verbatim string for a short Windows path and a raw literal for JSON, XML, SQL or any text with quotes; add
$$ when the text has braces of its own.
-
Mid to senior
A service builds a payment request body as an interpolated raw string with the amount in a hole. The payment API rejects every request from the servers in one region with "invalid JSON", while the same build works everywhere else. What happened, how do you prove it, and what do you change?
-
Mid to senior
A method that runs about 50,000 times a second builds a cache key with string.Format("{0}:{1}", userId, region), and a memory profile shows it among the top allocators. What does each call allocate, would an interpolated string help, and how would you measure the change?
-
Fresher to mid
A teammate writes SQL queries as raw interpolated literals over several lines and puts values typed by users into the holes. What is good about the raw literal, what is dangerous about the holes, and how should the query be written instead?
Key takeaways
- A hole is
{expression,width:format}: a positive width right-aligns, a negative one left-aligns, and a width never cuts a longer value. - An interpolated string formats numbers and dates with the current culture unless the code names another one.
- A raw string literal needs no escapes, removes the indentation of its closing quotes from every line, and with
$$keeps single braces as text. - A
u8literal is aReadOnlySpan<byte>that the compiler encodes as UTF-8.
Terms in this lesson
- interpolated string
- A string literal that starts with $ and holds holes in braces, such as $"{qty,4}": each hole is an expression, with an optional width and format string, whose formatted value replaces it in the text.
- raw string literal
- A string literal delimited by three or more quotes, """…""", that needs no escapes. Over several lines, the white space before its closing quotes is removed from every line.
- UTF-8 literal
- A string literal with the suffix u8, such as "OK"u8: the compiler encodes its text as UTF-8 and the literal is a ReadOnlySpan<byte> of those bytes.
References
- $ - string interpolation (C# reference) (Microsoft Learn)
- Strings (C# programming guide): raw and verbatim string literals (Microsoft Learn)
- Built-in reference types: string literals and UTF-8 string literals (Microsoft Learn)
- Composite formatting (width and format string components) (Microsoft Learn)
- FormattableString.Invariant method (Microsoft Learn)
- String.Create method (Microsoft Learn)
- DefaultInterpolatedStringHandler struct (Microsoft Learn)
- What's new in C# 13 (the \e escape sequence) (Microsoft Learn)
- What's new in C# 11 (raw string literals, newlines in interpolations, UTF-8 literals) (Microsoft Learn)
- How to serialize JSON in C# (Microsoft Learn)
- OWASP SQL Injection Prevention Cheat Sheet (OWASP)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress