Day 8: Strings in Depth - Every Trick for Slicing, Searching, and Formatting Text
arrow_back All Posts
April 06, 2026 7 min read .NET/C#

Day 8: Strings in Depth - Every Trick for Slicing, Searching, and Formatting Text

Welcome back! On Day 7 you learned to stop repeating yourself with methods. Today we're diving deep into one of the most-used types in all of programming: strings. You've been using them since Day 1 โ€” but trust me, you've barely scratched the surface. ๐Ÿ”ค

1. What Even Is a String, Really?

You already know a string is text in double quotes: "Hello". But under the hood, a string in C# is an immutable sequence of characters. Think of it like a frozen necklace of beads โ€” each bead is a char, and once the necklace is made, you can't swap individual beads. You can only make a new necklace.

string greeting = "Hello";
// greeting[0] = 'J';  // โŒ Compiler error! Strings are immutable.

Immutable means once a string is created, it cannot be changed. Every "modification" actually creates a brand-new string. This matters for performance later โ€” but for now, just know: strings are read-only.

2. The String Toolkit โ€” Essential Methods

C# gives you a massive toolbox for working with strings. Here are the greatest hits:

string name = "  Farhad Mammadov  ";

Console.WriteLine(name.Length);            // 19 (includes spaces)
Console.WriteLine(name.Trim());            // "Farhad Mammadov"
Console.WriteLine(name.Trim().ToUpper());  // "FARHAD MAMMADOV"
Console.WriteLine(name.Trim().ToLower());  // "farhad mammadov"

These methods return new strings โ€” they don't modify the original (remember: immutable!).

Here's a quick reference of the methods you'll use every day:

  • Trim() โ€” removes whitespace from both ends
  • TrimStart() / TrimEnd() โ€” removes whitespace from one end
  • ToUpper() / ToLower() โ€” changes case
  • Contains("text") โ€” returns true if the substring exists
  • StartsWith("text") / EndsWith("text") โ€” checks prefix/suffix
  • IndexOf("text") โ€” returns position of first match, or -1 if not found
  • Replace("old", "new") โ€” swaps all occurrences
  • Insert(index, "text") โ€” inserts text at a position
  • Remove(startIndex, count) โ€” removes characters
  • PadLeft(totalWidth) / PadRight(totalWidth) โ€” pads with spaces (great for alignment)

3. Searching Inside Strings

Need to find something? You've got options:

string sentence = "The quick brown fox jumps over the lazy dog";

Console.WriteLine(sentence.Contains("fox"));       // True
Console.WriteLine(sentence.StartsWith("The"));     // True
Console.WriteLine(sentence.EndsWith("cat"));       // False
Console.WriteLine(sentence.IndexOf("brown"));      // 10
Console.WriteLine(sentence.LastIndexOf("the"));    // 31

Pro tip: Contains, StartsWith, and EndsWith all accept a StringComparison parameter for case-insensitive searches:

bool found = sentence.Contains("FOX", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(found);  // True

Always use StringComparison.OrdinalIgnoreCase instead of converting to upper/lower first โ€” it's faster and handles edge cases in other languages better.

4. Slicing with Substring and Ranges

Need a piece of a string? Two approaches:

string full = "Hello, World!";

// Classic: Substring(startIndex, length)
string world = full.Substring(7, 5);
Console.WriteLine(world);  // "World"

// Modern C# way: Range operator (C# 8+)
string worldRange = full[7..12];
Console.WriteLine(worldRange);  // "World"

// From index to end
string fromComma = full[5..];
Console.WriteLine(fromComma);  // ", World!"

// Last N characters using ^
string lastSix = full[^6..];
Console.WriteLine(lastSix);  // "World!"

The range operator (..) is the modern, cleaner way. ^ means "from the end." full[^6..] reads as "start 6 from the end, go to the end."

5. Splitting and Joining

Splitting a string into pieces and putting them back together โ€” bread and butter of text processing:

string csv = "apple,banana,cherry,date";
string[] fruits = csv.Split(',');

foreach (string fruit in fruits)
{
    Console.WriteLine(fruit);
}
// apple
// banana
// cherry
// date

// Join them back with a different separator
string result = string.Join(" | ", fruits);
Console.WriteLine(result);  // "apple | banana | cherry | date"

Split also handles multiple separators and can remove empty entries:

string messy = "one,,two,,,three";
string[] clean = messy.Split(',', StringSplitOptions.RemoveEmptyEntries);
// clean = ["one", "two", "three"]

6. String Interpolation โ€” The Right Way to Build Strings

You already know $"Hello, {name}!" from Day 2. But interpolation can do more:

decimal price = 19.99m;
int quantity = 3;

// Format specifiers inside interpolation
Console.WriteLine($"Total: {price * quantity:C}");      // Total: $59.97 (currency)
Console.WriteLine($"Tax: {0.08675309:P2}");             // Tax: 8.68% (percent, 2 decimals)
Console.WriteLine($"Hex: {255:X}");                     // Hex: FF
Console.WriteLine($"Padded: {42:D5}");                  // Padded: 00042
Console.WriteLine($"Date: {DateTime.Now:yyyy-MM-dd}");  // Date: 2026-04-06

The format specifier goes after the : inside the braces. The most common ones:

  • C โ€” currency
  • N2 โ€” number with 2 decimal places and thousand separators
  • P โ€” percentage
  • D5 โ€” integer padded to 5 digits
  • X โ€” hexadecimal
  • yyyy-MM-dd โ€” date formatting

7. StringBuilder โ€” When Performance Matters

Remember how strings are immutable? That means this innocent-looking loop creates 1,000 temporary string objects:

// โŒ Slow โ€” creates a new string every iteration
string result = "";
for (int i = 0; i < 1000; i++)
{
    result += $"Line {i}\n";
}

Enter StringBuilder โ€” a mutable buffer that builds strings efficiently:

using System.Text;

// โœ… Fast โ€” modifies the same buffer
StringBuilder sb = new();
for (int i = 0; i < 1000; i++)
{
    sb.AppendLine($"Line {i}");
}
string result = sb.ToString();

Rule of thumb: if you're concatenating in a loop or building a string from many pieces, use StringBuilder. For 2-3 concatenations? Regular interpolation is fine.

Key StringBuilder methods:

  • Append("text") โ€” adds to the end
  • AppendLine("text") โ€” adds text + newline
  • Insert(index, "text") โ€” inserts at position
  • Replace("old", "new") โ€” replaces in-place
  • Clear() โ€” empties the buffer
  • ToString() โ€” produces the final string

8. Verbatim and Raw String Literals

Tired of escaping backslashes in file paths? C# has you covered:

// Regular string โ€” must escape backslashes
string path1 = "C:\\Users\\farhad\\Documents";

// Verbatim string (@) โ€” backslashes are literal
string path2 = @"C:\Users\farhad\Documents";

// Raw string literal (C# 11+) โ€” triple quotes, no escaping needed
string json = """
    {
        "name": "Farhad",
        "blog": "farhad.su"
    }
    """;

Raw string literals ("""...""") are the newest addition. They preserve indentation relative to the closing """ and need zero escaping. Perfect for JSON, SQL, HTML, or any multi-line text.

You can even use interpolation in raw strings with multiple $ signs:

string name = "Farhad";
string raw = $$"""
    {
        "name": "{{name}}",
        "year": 2026
    }
    """;

The number of $ signs tells C# how many braces trigger interpolation. $$ means {{ }} is the interpolation syntax, so literal { } in JSON doesn't cause problems.

9. Comparing Strings โ€” It's More Nuanced Than You Think

string a = "hello";
string b = "Hello";

// Case-sensitive (default)
Console.WriteLine(a == b);                                        // False
Console.WriteLine(string.Equals(a, b));                           // False

// Case-insensitive
Console.WriteLine(string.Equals(a, b, StringComparison.OrdinalIgnoreCase));  // True

// For sorting/display โ€” use culture-aware comparison
Console.WriteLine(string.Compare(a, b, StringComparison.CurrentCulture));

Best practices:

  • Use == or string.Equals for exact matches
  • Use StringComparison.OrdinalIgnoreCase for case-insensitive logic (usernames, emails, etc.)
  • Use StringComparison.CurrentCulture only when you need locale-aware sorting
  • Never use ToLower() == ToLower() for comparison โ€” it's slower and breaks in Turkish (the infamous Turkish-ฤฐ problem)

10. Your Homework: Word Counter

Build a program that takes a sentence and reports:

  1. Total character count (with and without spaces)
  2. Word count
  3. The longest word
  4. The sentence reversed (word by word, not character by character)
string input = "The quick brown fox jumps over the lazy dog";

// Character counts
Console.WriteLine($"Characters (with spaces): {input.Length}");
Console.WriteLine($"Characters (no spaces): {input.Replace(" ", "").Length}");

// Word count
string[] words = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine($"Words: {words.Length}");

// Longest word
string longest = "";
foreach (string word in words)
{
    if (word.Length > longest.Length)
        longest = word;
}
Console.WriteLine($"Longest word: {longest}");

// Reverse word order
Array.Reverse(words);
Console.WriteLine($"Reversed: {string.Join(" ", words)}");

Bonus challenge: make the word count case-insensitive and count unique words only. (Hint: you'll want a HashSet<string> โ€” we haven't covered it yet, but new HashSet<string>(words, StringComparer.OrdinalIgnoreCase).Count does the trick.)

For the complete string reference, see the System.String docs on Microsoft Learn.

Summary of Day 8

  • Strings are immutable โ€” every modification creates a new string.
  • The string toolkit (Trim, Contains, Replace, Split, Join) covers 90% of everyday text work.
  • Range operators ([7..12], [^6..]) are the modern way to slice strings.
  • String interpolation with format specifiers ({value:C}, {value:N2}) handles formatting elegantly.
  • StringBuilder is your friend for loops and heavy concatenation โ€” avoids creating thousands of throwaway strings.
  • Raw string literals ("""...""") eliminate escaping headaches for JSON, paths, and multi-line text.
  • Always use StringComparison.OrdinalIgnoreCase instead of ToLower() for case-insensitive comparisons.

Tomorrow: we'll talk about Error Handling with try/catch โ€” because your code will crash, and the difference between a junior and a senior dev is how gracefully they handle it. Time to stop letting exceptions ruin your day. ๐Ÿ’ฅ

See you on Day 9!

Share
FM

Farhad Mammadov

.NET Engineer & Cloud Architect ยท Bayern, Germany. Writing about scalable backend systems, AWS, and SRE.