DEV Community

Cover image for 5 C# string bugs that only happen on someone else's machine
Naze Code
Naze Code

Posted on

5 C# string bugs that only happen on someone else's machine

Every snippet in this post passes on a developer machine set to US English. Then someone in Istanbul, Berlin or London runs it, or data arrives from another system, and it quietly does the wrong thing.

To reproduce that, the demo sets the culture per "user" before each run. Every output below is copied from actually running it on .NET 10.

1. ToUpper() and the Turkish i

string command = "quit";   // what the user typed

if (command.ToUpper() == "QUIT")
    Console.WriteLine("Bye!");
else
    Console.WriteLine($"Unknown command: {command}");
Enter fullscreen mode Exit fullscreen mode

On a US machine: Bye!. On a machine set to Turkish:

Unknown command: quit
"quit".ToUpper() = QUİT
Enter fullscreen mode Exit fullscreen mode

Turkish has a dotted and a dotless i, so the uppercase of i is İ, not I. ToUpper() follows the current culture, so the comparison fails.

Fix: compare without changing case at all.

if (command.Equals("quit",
        StringComparison.OrdinalIgnoreCase))
    Console.WriteLine("Bye!");
Enter fullscreen mode Exit fullscreen mode
Bye!
Enter fullscreen mode Exit fullscreen mode

2. double.Parse on a config value

string input = "3.14";   // from a config file

double price = double.Parse(input);

Console.WriteLine($"Price: {price}");
Enter fullscreen mode Exit fullscreen mode
en-US: Price: 3.14
de-DE: Price: 314
Enter fullscreen mode Exit fullscreen mode

In German, . is the thousands separator, so 3.14 reads as three hundred fourteen. No exception, just a price a hundred times too high.

Fix: text written by a machine should be parsed with a fixed culture.

double price = double.Parse(input,
    CultureInfo.InvariantCulture);
Enter fullscreen mode Exit fullscreen mode
de-DE: Price: 3,14
en-US: Price: 3.14
Enter fullscreen mode Exit fullscreen mode

The value is now right everywhere. Displaying it as 3,14 to a German user is correct: localize what people read, never what machines read.

3. DateTime.Parse on 03/04/2026

string shipped = "03/04/2026";   // written by the US office

var date = DateTime.Parse(shipped);

Console.WriteLine($"Shipped: {date:MMMM d}");
Enter fullscreen mode Exit fullscreen mode
en-US: Shipped: March 4
en-GB: Shipped: April 3
Enter fullscreen mode Exit fullscreen mode

Same string, two dates a month apart, and both parse successfully.

Fix: store dates as ISO 8601 and parse them with an exact format.

string shipped = "2026-03-04";   // ISO 8601: no guessing

var date = DateTime.ParseExact(shipped, "yyyy-MM-dd",
    CultureInfo.InvariantCulture);
Enter fullscreen mode Exit fullscreen mode
en-GB: Shipped: March 4
Enter fullscreen mode Exit fullscreen mode

4. Dictionary keys and capital letters

var users = new Dictionary<string, string>
{
    ["ada@x.dev"] = "Ada",
};

string login = "Ada@X.dev";   // what the user typed

Console.WriteLine(users.TryGetValue(login, out var name)
    ? $"Welcome, {name}"
    : "User not found");
Enter fullscreen mode Exit fullscreen mode
User not found
Enter fullscreen mode Exit fullscreen mode

A Dictionary<string, …> compares keys exactly, so case matters.

Fix: give the dictionary a comparer.

var users = new Dictionary<string, string>(
    StringComparer.OrdinalIgnoreCase)
{
    ["ada@x.dev"] = "Ada",
};
Enter fullscreen mode Exit fullscreen mode
Welcome, Ada
Enter fullscreen mode Exit fullscreen mode

Why not just call ToLower() on every key? Because of trap 1 again. On a Turkish machine:

"QUIT".ToLower() = quıt
Enter fullscreen mode Exit fullscreen mode

5. Two strings that look identical

string typed    = "café";           // typed on a keyboard
string imported = "café";     // e + combining accent

Console.WriteLine(typed);
Console.WriteLine(imported);
Console.WriteLine(typed == imported);
Enter fullscreen mode Exit fullscreen mode
café
café
False
Enter fullscreen mode Exit fullscreen mode

The first é is one character. The second is a plain e followed by a combining accent: lengths 4 and 5. Text pasted from a Mac, a PDF or another system often arrives in the second form.

Fix: normalize before comparing or storing.

bool same = typed.Normalize() == imported.Normalize();
Enter fullscreen mode Exit fullscreen mode
True
Enter fullscreen mode Exit fullscreen mode

(A culture-aware string.Equals(a, b, StringComparison.CurrentCulture) already treats them as equal. But ==, dictionary keys, hash sets and most database indexes compare ordinally, so normalize once on the way in.)

Recap

  1. Compare with StringComparison.OrdinalIgnoreCase, not ToUpper() or ToLower().
  2. Parse machine-written numbers with CultureInfo.InvariantCulture.
  3. Store dates as ISO 8601 and use ParseExact.
  4. Give string-keyed dictionaries a comparer.
  5. Normalize() text that comes from somewhere else.

Has a culture bug ever bitten you in production? Tell me which one in the comments.

I make short, tested videos about C# bugs that compile fine and still break things. More on the Naze Code YouTube channel.


Sources: Microsoft Learn, Best practices for comparing strings, CultureInfo.InvariantCulture, String.Normalize. Tested on .NET SDK 10.0.302 on Windows (ICU globalization), with the culture set per run.

Top comments (0)