C# strings and formatting

Use common string methods, compare text correctly, and format numbers with format specifiers.

C#
using System;
using System.Text;

class Program {
  static void Main() {
    string name = "Programonic";
    Console.WriteLine(name.Length + " " + name.Substring(0, 7) + " " + name.IndexOf('m'));
    Console.WriteLine(name.ToUpper() + " " + name.Replace("o", "0"));

    string[] parts = "a,b,,c".Split(',');
    Console.WriteLine(parts.Length + " " + string.Join("-", parts));
    Console.WriteLine("  padded ".Trim().PadLeft(8, '*'));

    Console.WriteLine("apple" == "APPLE");
    Console.WriteLine(string.Equals("apple", "APPLE", StringComparison.OrdinalIgnoreCase));

    double price = 1234.56;
    Console.WriteLine(FormattableString.Invariant($"{price:F2} {price:N0} {42:D5} {255:X}"));

    var builder = new StringBuilder();
    for (int i = 1; i <= 3; i++) {
      builder.Append(i).Append(i < 3 ? "," : "");
    }
    Console.WriteLine(builder);
  }
}
Output
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
False
True
1234.56 1,235 00042 FF
1,2,3

Explanation

A C# string is an immutable sequence of characters, so methods such as Substring, Replace, and ToUpper return new strings. Split turns a string into an array, string.Join does the reverse, and Trim and PadLeft change the white space around the text. StringBuilder is the right tool for building a long string in a loop, because it does not copy the text on every append.

The == operator on strings compares the characters, and it is case-sensitive. For case-insensitive comparison use string.Equals with StringComparison.OrdinalIgnoreCase. Interpolated strings, written $"...", can include format specifiers after a colon: F2 for two decimals, N0 for a thousands separator, D5 for five digits with leading zeros, and X for hexadecimal. Formatting depends on the culture, so FormattableString.Invariant fixes the output regardless of the computer's settings.

Explore the dry run
Guided walkthroughReady
1using System;
2using System.Text;
3
4class Program {
5 static void Main() {
6 string name = "Programonic";
7 Console.WriteLine(name.Length + " " + name.Substring(0, 7) + " " + name.IndexOf('m'));
8 Console.WriteLine(name.ToUpper() + " " + name.Replace("o", "0"));
9
10 string[] parts = "a,b,,c".Split(',');
11 Console.WriteLine(parts.Length + " " + string.Join("-", parts));
12 Console.WriteLine(" padded ".Trim().PadLeft(8, '*'));
13
14 Console.WriteLine("apple" == "APPLE");
15 Console.WriteLine(string.Equals("apple", "APPLE", StringComparison.OrdinalIgnoreCase));
16
17 double price = 1234.56;
18 Console.WriteLine(FormattableString.Invariant($"{price:F2} {price:N0} {42:D5} {255:X}"));
19
20 var builder = new StringBuilder();
21 for (int i = 1; i <= 3; i++) {
22 builder.Append(i).Append(i < 3 ? "," : "");
23 }
24 Console.WriteLine(builder);
25 }
26}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Length 11, the first seven characters, and the index of the first m.—
11 Program 6
Both methods return new strings; name itself is unchanged.—
11 Program 6
PROGRAMONIC Pr0gram0nic
Split keeps the empty entry between the commas.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
Trim removes the spaces; PadLeft fills the left side up to width 8.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
== is case-sensitive.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
False
Explicit comparison option ignores the case.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
False
True
Fixed-point with 2 decimals, thousands separator with no decimals, five-digit zero padding, and hexadecimal.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
False
True
1234.56 1,235 00042 FF
StringBuilder built the text without creating a new string on every append.—
11 Program 6
PROGRAMONIC Pr0gram0nic
4 a-b--c
**padded
False
True
1234.56 1,235 00042 FF
1,2,3

Cost and common mistakes

Most string operations are O(n) in the length of the text.

  • Using + in a loop to build a long string creates a new string every time, which is O(n²) overall. Use StringBuilder.
  • Without an explicit culture, number formatting follows the computer's regional settings, so the same program can print 1,234.50 or 1.234,50.
  • Use the Ordinal comparisons for identifiers and file names, and culture-aware comparisons only for text shown to people.

Try it yourself

How would you print 7 as 007?

Reveal answer and reasoning

Use the D3 format specifier: 7.ToString("D3") gives 007.

Further reading