Strings & Interpolation
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.
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);6 J r Jai JAIPUR True 3 Jaisalmer Jaipur
city[0]is the first character, achar.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 writecity[2..](from index 2 to the end) orcity[1..4].IndexOfreturns the position of the first match, or-1if 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.
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'));Aarav Sharma True True Rohtak | Jaipur | Pune 00042
Trim()removes spaces from both ends (TrimStartandTrimEnddo one side).Splitbreaks a string into an array of parts.StringSplitOptions.TrimEntriesalso trims each part.string.Joindoes the opposite: it glues parts together with a separator.string.IsNullOrWhiteSpaceis the safest way to check that a user typed something real.EqualswithStringComparison.OrdinalIgnoreCasecompares text without caring about capital letters. Use this rather than callingToLower()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.
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}|");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|
| Specifier | Meaning | Example result |
|---|---|---|
F2 | Fixed-point, 2 decimal places | 1299.50 |
N0 | Number with group separators, 0 decimals | 1,299 |
D5 | Integer padded with zeros to 5 digits | 00003 |
C | Currency of the current culture | depends on settings |
,10 / ,-10 | Right / left align in 10 characters | used 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:
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));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.
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);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:
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());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; writename = name.Trim();. - Index out of range.
"Pune"[4]throwsIndexOutOfRangeException, because the last index isLength - 1. - Comparing with
ToLower()everywhere. Usestring.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,ReplaceandTrimreturn new strings. - Use
s[i]for a character,s[^1]for the last one and ranges likes[..3]for parts. Split,Join,Trim,Contains,IndexOfandPadLeftcover most everyday text work.$"..."interpolation supports format specifiers (:F2,:N0,:D5) and alignment (,10).- Number formats depend on culture; use
CultureInfo("en-IN")orInvariantCultureexplicitly. - Raw string literals (
""") hold multi-line text;StringBuilderbuilds 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
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}|");|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?
^1 means the first position from the end, which is the last character.
2. After string s = "pune"; s.ToUpper();, what is s?
Strings are immutable; ToUpper returns a new string, and here the result was not stored.
3. What does $"{7.456:F2}" produce?
F2 formats the number with exactly two decimal places, rounding the last digit.
4. Which literal is best for a Windows path like C:\temp\new?
A verbatim string treats backslashes literally, so \t and \n are not escape sequences.
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
