Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Beginner
  • 15 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 5 of 15 minutes free
Free: Interpolation puts formatted values into a string · Raw string literals keep quotes, backslashes and braces as they are · UTF-8 literals are bytes that the compiler encodes. With a pass: 3 in-depth parts and 3 interview answers, about 10 minutes.

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

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:

bill.cs: a bill laid out with widths and format strings C# · bill/bill.cs
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:

Interactive · stage by stage How the hole {price,10:N2} becomes text Step through one hole of the bill, for the paneer wrap at 189.5, from the value to the ten chars it adds to the line.
How the hole {price,10:N2} becomes text, step 5 of 5. Showing The expression, The format string, N2 (gives 189.50), The current culture, The width, 10 (" 189.50"), Added to the line (Paneer wrap 1 189.50). In focus: Added to the line.The expressionprice is 189.5mThe format string, N2gives 189.50The current cultureinvariant in bill.csThe width, 10" 189.50"Added to the linePaneer wrap 1 189.50

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Evaluate. The part before the comma is any C# expression. Here it is the variable price, a decimal that holds 189.5.
  2. 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.
  3. 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.
  4. 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.
  5. 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

  1. Question 1 of 2 What does $"[{"ab",-5}]" produce?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: [ab ]

    In {expression,width}, a positive width right-aligns the value and a negative one left-aligns it, padding with spaces up to the width.

  2. Question 2 of 2 A program never sets a culture and prints $"{total:N2}". What decides whether the decimals follow a dot or a comma?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: CultureInfo.CurrentCulture, the culture of the computer or thread it runs on

    An interpolated string formats every hole with CultureInfo.CurrentCulture by default. Name the culture whenever the text must be the same everywhere.

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:

literals.cs: one path and one quote three ways, then a JSON template C# · literals/literals.cs
// 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
String Escape / Unescape Choose C# and paste a JSON snippet to see the escaped regular literal that a raw literal saves you from writing.

Checkpoint

Check: Raw and verbatim string literals

  1. Question 1 of 2 The closing quotes stand 4 spaces in. How many spaces does the line " b" (6 spaces in the source) start with in the string?

    Read the code, then choose one answer.

    string s = """
        a
          b
        """;
    Show the answer: Question 1 of 2

    Answer: 2

    In a raw literal over several lines, the white space to the left of the closing quotes is removed from every line, which is how the JSON in literals.cs started at the margin.

  2. Question 2 of 2 In $$"""{ "qty": {{qty}} }""", which braces are a hole?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: {{qty}}

    The number of dollar signs sets how many braces open a hole. $$ leaves single braces as text, which is what a JSON template needs.

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:

utf8.cs: a u8 literal against Encoding.UTF8.GetBytes C# · utf8/utf8.cs
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

  1. One question How many bytes is the u8 literal "Asha नमस्ते"u8? Each ASCII char takes 1 byte and each of the 6 Devanagari chars 3 bytes.

    Type a number.

    A hint

    "Asha " is 5 ASCII chars, the space included.

    Show the answer: One question

    Answer: 23 bytes

    5 ASCII chars take 5 bytes and the 6 Devanagari chars 6 × 3 = 18, as utf8.cs counted for नमस्ते: 5 + 18 = 23.

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.

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.

  1. Question 1 of 8 In the hole {price,10:N2}, what does the 10 do?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: It pads the formatted price to at least 10 chars, on the left

    A hole is {expression,width:format}. The width is a minimum, and its sign chooses the side the padding goes on.

  2. Question 2 of 8 With the invariant culture, what does $"{0.265m:P1}" give?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: 26.5 %

    The format string sets the kind of number and its decimals, and the culture sets the symbols and spacing, so the same hole can print 26.5 % or 26.5% depending on the culture.

  3. Question 3 of 8 An item name is 15 chars long and goes in {item,-12}. What happens to the columns after it on that line?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: They shift 3 chars to the right, because the name is kept whole

    For fixed columns, keep values within their widths: cut a long name with a range such as item[..12], or choose a wider column.

  4. Question 4 of 8 Which literal is the path C:\temp\new.txt?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: @"C:\temp\new.txt"

    Regular literals read escape sequences; verbatim and raw literals do not. Paths are the classic case for @"…".

  5. Question 5 of 8 Which literal compiles and holds exactly the text Ravi said "done". with its two quotes?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: """Ravi said "done"."""

    Each kind of literal has its own rule for a quote inside: \" in a regular literal, "" in a verbatim one, and nothing at all in a raw literal, which is why raw literals suit JSON and other text full of quotes.

  6. Question 6 of 8 A test needs a 20-line JSON payload with quotes, nested braces and two values filled in. Which literal fits best?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: A raw interpolated literal starting with $$"""

    Raw interpolated literals were made for this case: the text reads as it will be printed, and $$ keeps the JSON braces as text.

  7. Question 7 of 8 What is the type of "text"u8?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: ReadOnlySpan<byte>

    The compiler encodes the literal as UTF-8 and hands it out as a ReadOnlySpan<byte>, so the program does no encoding when it runs.

  8. Question 8 of 8 Which use of a u8 literal is fine?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: Writing "OK\r\n"u8 to a stream

    UTF-8 literals are fixed bytes for writing out. They have no holes, and their span type limits where they can be kept.

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.

  1. 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?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. 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?

    Model answer, Pro. First step and follow-ups

  3. 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?

    Model answer, Pro. First step and follow-ups

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 u8 literal is a ReadOnlySpan<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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.