Porównywanie znaków, ToUpper, string.IndexOf oraz StringComparison.Ordinal

Resharper daje naprawdę cenne wskazówki. Nie wszystkie są oczywiste i czasami należy zagłębić się w temat. Jedną z takich wskazówek jest używanie IndexOf wraz z StringComparison.Ordinal.

Załóżmy, że mamy następujący kod:

string text = "test";
Console.WriteLine(text.IndexOf("est"));

Resharper zasugeruje konwersję do:

string text = "test";
Console.WriteLine(text.IndexOf("est", StringComparison.Ordinal));

Dlaczego?
Jeśli nie przekażemy ustawień regionalnych jawnie, wtedy domyślnie aktualna zostanie użyta.
Czasami oczywiście dokładnie tego chcemy i dlatego domyślnie przekazywany jest StringComparison.CurrentCulture.

W niektórych sytuacjach może spowodować to bardzo irytujące problemy. Każdy język ma pewne zasady, które nie zawsze pokrywają się z intuicją osoby piszącącej kod. Najsłynniejszym chyba przykładem jest język turecki i litera ‘i’.

Czego byśmy spodziewali się po poniższym kodzie?

string text = "some text this";
Console.WriteLine(text.ToUpper().IndexOf("THIS")); 

Naturalne wydaje się, że po wywołaniu ToUpper, tekst “THIS” zostanie znaleziony (na pozycji 10). Zmieńmy kulturę na tr-TR (Turcja):

Thread.CurrentThread.CurrentCulture=new CultureInfo("tr-TR");
string text = "some text this";
Console.WriteLine(text.ToUpper().IndexOf("THIS"));

Na ekranie zobaczymy -1. W języku tureckim, wielka litera ‘i’ to İ, a nie ‘I’.

Inny przykład to w niemieckim litery ‘ß’ oraz ‘ss’, które będą traktowane jako takie same, jeśli ustawimy odpowiednio kulturę.

Ciekawy przykład, znalazłem również na StackOverflow:

//SOURCE: http://stackoverflow.com/a/10941507
var s1 = "é"; //é as one character (ALT+0233)
var s2 = "é"; //'e', plus combining acute accent U+301 (two characters)

Console.WriteLine(s1.IndexOf(s2, StringComparison.Ordinal)); //-1
Console.WriteLine(s1.IndexOf(s2, StringComparison.InvariantCulture)); //0
Console.WriteLine(s1.IndexOf(s2, StringComparison.CurrentCulture)); //0

Problem w tym, że nie mamy pojęcia na jakim komputerze nasz kod jest wykonywany. Musimy przyjmować, że może być to dowolna kultura. Jeśli zatem, porównujemy angielskie czy polskie znaki, wtedy nie chcemy korzystać z ustawień regionalnych. Co jeśli aplikacja webowa jest hostowana na niemieckim serwerze, a porównujemy w niej wyłącznie polskie słowa, bo np. baza danych i aplikacja jest wykorzystywana wyłącznie w Polsce. Z tego względu, bezpieczniejszą opcją jest StringComparison.Ordinal, która porównuje wartości liczbowe znaków (np. kod ASCII).

2 thoughts on “Porównywanie znaków, ToUpper, string.IndexOf oraz StringComparison.Ordinal”

Leave a Reply

Your email address will not be published.