Strings & Interpolation

C# · Lesson 7 of 31 7 min read Updated

Names, addresses, messages, file paths, JSON: a huge part of every program is text. In C#, text is stored in the string type, and the language gives you a rich set of methods to search, clean, split and build strings, plus string interpolation, the neat $"..." syntax you have already used.

In this lesson you will learn how strings work, the methods you will use most, how to format numbers inside text, and the modern raw string literals that make multi-line text easy.

String basics

A string is a sequence of characters. You can get its length, read single characters by index (counting from 0), and take parts of it with ranges.

C#
string city = "Jaipur";

Console.WriteLine(city.Length);
Console.WriteLine(city[0]);
Console.WriteLine(city[^1]);
Console.WriteLine(city[..3]);
Console.WriteLine(city.ToUpper());
Console.WriteLine(city.Contains("pur"));
Console.WriteLine(city.IndexOf('p'));
Console.WriteLine(city.Replace("pur", "salmer"));
Console.WriteLine(city);
Output
6
J
r
Jai
JAIPUR
True
3
Jaisalmer
Jaipur
  • city[0] is the first character, a char. city[^1] means "first from the end", the last character.
  • city[..3] is a range: characters from the start up to, but not including, index 3. You can also write city[2..] (from index 2 to the end) or city[1..4].
  • IndexOf returns the position of the first match, or -1 if there is none.

The last line shows an important fact: strings are immutable. ToUpper and Replace do not change city; they return a new string. If you want to keep the result, assign it: city = city.ToUpper();.

Cleaning and splitting text

Text typed by users is messy: extra spaces, odd capital letters, commas in strange places. These methods tidy it up.

C#
string raw = "  aarav SHARMA  ";
string[] parts = raw.Trim().Split(' ');

string first = char.ToUpper(parts[0][0]) + parts[0][1..].ToLower();
string last = char.ToUpper(parts[1][0]) + parts[1][1..].ToLower();
Console.WriteLine($"{first} {last}");

Console.WriteLine(string.IsNullOrWhiteSpace("   "));
Console.WriteLine("PUNE".Equals("pune", StringComparison.OrdinalIgnoreCase));

string csv = "Rohtak, Jaipur ,Pune";
string[] cities = csv.Split(',', StringSplitOptions.TrimEntries);
Console.WriteLine(string.Join(" | ", cities));
Console.WriteLine("42".PadLeft(5, '0'));
Output
Aarav Sharma
True
True
Rohtak | Jaipur | Pune
00042
  • Trim() removes spaces from both ends (TrimStart and TrimEnd do one side).
  • Split breaks a string into an array of parts. StringSplitOptions.TrimEntries also trims each part.
  • string.Join does the opposite: it glues parts together with a separator.
  • string.IsNullOrWhiteSpace is the safest way to check that a user typed something real.
  • Equals with StringComparison.OrdinalIgnoreCase compares text without caring about capital letters. Use this rather than calling ToLower() on both sides.

String interpolation and format specifiers

An interpolated string starts with $. Any expression inside { } is evaluated and inserted. After a colon you can add a format specifier, and after a comma an alignment width.

C#
string item = "Cricket bat";
decimal price = 1299.5m;
int qty = 3;
double rating = 4.567;

Console.WriteLine($"{item}: Rs {price:F2} x {qty} = Rs {price * qty:F2}");
Console.WriteLine($"Rating: {rating:F1} out of 5");
Console.WriteLine($"Order no. {qty:D5}");
Console.WriteLine($"|{"Bat",-10}|{price,10:F2}|");
Console.WriteLine($"|{"Ball",-10}|{249m,10:F2}|");
Output
Cricket bat: Rs 1299.50 x 3 = Rs 3898.50
Rating: 4.6 out of 5
Order no. 00003
|Bat       |   1299.50|
|Ball      |    249.00|
SpecifierMeaningExample result
F2Fixed-point, 2 decimal places1299.50
N0Number with group separators, 0 decimals1,299
D5Integer padded with zeros to 5 digits00003
CCurrency of the current culturedepends on settings
,10 / ,-10Right / left align in 10 charactersused for tables

Alignment is perfect for printing neat bills and reports in the console.

Culture: commas, dots and the rupee sign

Some formats depend on the computer's culture (its language and region settings). In India, large numbers are grouped as lakhs and crores; in the US, in thousands. You can choose a culture explicitly with CultureInfo:

C#
using System.Globalization;

Console.OutputEncoding = System.Text.Encoding.UTF8; // so the rupee sign displays on Windows

decimal amount = 1250000m;
var india = new CultureInfo("en-IN");

Console.WriteLine(amount.ToString("N0", india));
Console.WriteLine(amount.ToString("N0", CultureInfo.InvariantCulture));
Console.WriteLine(amount.ToString("C", india));
Output
12,50,000
1,250,000
₹12,50,000.00

CultureInfo.InvariantCulture gives the same result on every computer, which is what you want for files and data exchange. Use a specific culture such as en-IN for text that people read. Without the OutputEncoding line, some Windows terminals print ? instead of ₹.

Special characters and raw string literals

Inside a normal string, a backslash starts an escape sequence: \n is a new line, \t a tab, \" a double quote and \\ a backslash. A verbatim string, starting with @, treats backslashes as plain characters, which is handy for Windows paths.

Raw string literals (C# 11) start and end with three or more double quotes. Everything inside is taken literally, including quotes and line breaks, and the indentation of the closing """ is removed from every line.

C#
Console.WriteLine("Name: Meera\nCity: Kochi");
Console.WriteLine("She said \"Namaste\"");

string path = @"C:\Users\Meera\marks.csv";
Console.WriteLine(path);

string name = "Meera";
string json = $$"""
    {
      "name": "{{name}}",
      "city": "Kochi"
    }
    """;
Console.WriteLine(json);
Output
Name: Meera
City: Kochi
She said "Namaste"
C:\Users\Meera\marks.csv
{
  "name": "Meera",
  "city": "Kochi"
}

With $$""", interpolation holes use double braces {{name}}, so single { and } stay as literal JSON braces. Raw strings are the cleanest way to write JSON, SQL and HTML inside C# code.

Building strings in a loop: StringBuilder

Because strings are immutable, adding to a string inside a loop creates a brand-new string each time. For a few joins that is fine, but for hundreds it wastes memory. StringBuilder (in System.Text) grows one buffer instead. Loops are covered in Loops; this one runs five times:

C#
using System.Text;

var table = new StringBuilder();
for (int i = 1; i <= 5; i++)
{
    table.Append($"7 x {i} = {7 * i}");
    table.AppendLine();
}
Console.Write(table.ToString());
Output
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35

Common mistakes

  • Expecting a method to change the string. name.Trim(); on its own does nothing useful; write name = name.Trim();.
  • Index out of range. "Pune"[4] throws IndexOutOfRangeException, because the last index is Length - 1.
  • Comparing with ToLower() everywhere. Use string.Equals(a, b, StringComparison.OrdinalIgnoreCase).
  • Forgetting the $. "Total: {total}" prints the braces literally.
  • Backslashes in paths. "C:\new" contains a new-line escape \n. Use @"C:\new" or forward slashes.

Key points

  • Strings are immutable; methods such as ToUpper, Replace and Trim return new strings.
  • Use s[i] for a character, s[^1] for the last one and ranges like s[..3] for parts.
  • Split, Join, Trim, Contains, IndexOf and PadLeft cover most everyday text work.
  • $"..." interpolation supports format specifiers (:F2, :N0, :D5) and alignment (,10).
  • Number formats depend on culture; use CultureInfo("en-IN") or InvariantCulture explicitly.
  • Raw string literals (""") hold multi-line text; StringBuilder builds long strings efficiently.

Exercise

A canteen order arrives as the text " masala dosa , 2, 85.50 " (item, quantity, price per plate). Split it, trim each part, convert the quantity to int and the price to decimal, and print a bill line where the item name is in title case and left-aligned in 15 characters, and the total is right-aligned in 10 characters with 2 decimals.

Show solution
C#
string order = " masala dosa , 2, 85.50 ";
string[] parts = order.Split(',', StringSplitOptions.TrimEntries);

string item = parts[0];
int qty = int.Parse(parts[1]);
decimal price = decimal.Parse(parts[2], System.Globalization.CultureInfo.InvariantCulture);

string[] words = item.Split(' ');
string title = string.Join(" ", words.Select(w => char.ToUpper(w[0]) + w[1..]));

Console.WriteLine($"|{title,-15}|{qty * price,10:F2}|");
Output
|Masala Dosa    |    171.00|

TrimEntries removes the spaces around each part. Parsing with InvariantCulture makes sure 85.50 is read with a dot as the decimal point on any computer. Select applies the capitalising code to each word; you will meet it properly in LINQ.

Quick Quiz

1. What does "Kochi"[^1] return?

2. After string s = "pune"; s.ToUpper();, what is s?

3. What does $"{7.456:F2}" produce?

4. Which literal is best for a Windows path like C:\temp\new?

Learn C# with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call