Сравнение строк в C#How to compare strings in C#

Сравнивая строки, вы хотите ответить на один из двух вопросов: "Равны ли две эти строки?"You compare strings to answer one of two questions: "Are these two strings equal?" или "В каком порядке должны следовать эти строки при их сортировке?".or "In what order should these strings be placed when sorting them?"

Однако эту задачу осложняют факторы, влияющие на сравнение строк:Those two questions are complicated by factors that affect string comparisons:

  • Вы можете выбрать порядковое или лингвистическое сравнение.You can choose an ordinal or linguistic comparison.
  • Вы можете указать, учитывается ли регистр.You can choose if case matters.
  • Вы можете выбрать сравнения для конкретного языка и региональных параметров.You can choose culture-specific comparisons.
  • Лингвистические сравнения зависят от языка и региональных параметров, а также от используемой платформы.Linguistic comparisons are culture and platform-dependent.

Примечание

Примеры C# в этой статье выполняются во встроенном средстве выполнения кода и на площадке Try.NET.The C# examples in this article run in the Try.NET inline code runner and playground. Нажмите на кнопку Выполнить, чтобы выполнить пример в интерактивном окне.Select the Run button to run an example in an interactive window. После выполнения кода вы можете изменить его и выполнить измененный код, снова нажав на кнопку Выполнить.Once you execute the code, you can modify it and run the modified code by selecting Run again. Либо в интерактивном окне выполняется измененный код, либо, если компиляция завершается с ошибкой, в интерактивном окне отображаются все сообщения об ошибках компилятора C#.The modified code either runs in the interactive window or, if compilation fails, the interactive window displays all C# compiler error messages.

При сравнении строк вы определяете их порядок.When you compare strings, you define an order among them. Сравнения используются для сортировки последовательности строк.Comparisons are used to sort a sequence of strings. Если последовательность имеет известный порядок, это упрощает поиск как для программного обеспечения, так и для пользователей.Once the sequence is in a known order, it is easier to search, both for software and for humans. Другие сравнения могут проверять совпадение строк.Other comparisons may check if strings are the same. Эти проверки тождественности похожи на проверки равенства, но позволяют игнорировать некоторые различия, например различия в регистре.These sameness checks are similar to equality, but some differences, such as case differences, may be ignored.

Порядковые сравнения по умолчаниюDefault ordinal comparisons

Ниже представлены самые распространенные операции по умолчанию:By default, the most common operations:

выполняют порядковое сравнение с учетом регистра и используют текущие значения языка и региональных параметров при необходимости.perform a case-sensitive ordinal comparison and, if necessary, use the current culture. Это показано в следующем примере:The following example demonstrates that:

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

result = root.Equals(root2, StringComparison.Ordinal);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}");               

При порядковых сравнениях строк по умолчанию лингвистические правила не учитываются.The default ordinal comparison doesn't take linguistic rules into account when comparing strings. В них сравнивается двоичное значение каждого объекта Char в двух строках.It compares the binary value of each Char object in two strings. Таким образом, в порядковом сравнении по умолчанию также учитывается регистр.As a result, the default ordinal comparison is also case-sensitive.

Обратите внимание, что проверка на равенство с использованием String.Equals, а также операторов == и != отличается от сравнения строк с использованием методов String.CompareTo и Compare(String, String).Note that the test for equality with String.Equals and the == and != operators differs from string comparison using the String.CompareTo and Compare(String, String) methods. Хотя в проверках на равенство выполняется порядковое сравнение с учетом регистра, метод сравнения выполняет сравнение с учетом регистра, с учетом языка и региональных параметров с использованием текущего значения языка и региональных параметров.While the tests for equality perform a case-sensitive ordinal comparison, the comparison methods perform a case-sensitive, culture-sensitive comparison using the current culture. Поскольку методы сравнения по умолчанию часто выполняют сравнения различных типов, рекомендуется всегда четко определять назначение кода путем вызова перегрузки, которая явно указывает тип выполняемого сравнения.Because the default comparison methods often perform different types of comparisons, we recommend that you always make the intent of your code clear by calling an overload that explicitly specifies the type of comparison to perform.

Порядковые сравнения без учета регистраCase-insensitive ordinal comparisons

Метод String.Equals(String, StringComparison) позволяет указать значение StringComparison для объекта StringComparison.OrdinalIgnoreCaseThe String.Equals(String, StringComparison) method enables you to specify a StringComparison value of StringComparison.OrdinalIgnoreCase для порядкового сравнения без учета регистра.for a case-insensitive ordinal comparison. Также имеется статический метод String.Compare(String, String, StringComparison), позволяющий проводить порядковое сравнение без учета регистра, если указать значение StringComparison.OrdinalIgnoreCase для аргумента StringComparison.There is also a static String.Compare(String, String, StringComparison) method that performs a case-insensitive ordinal comparison if you specify a value of StringComparison.OrdinalIgnoreCase for the StringComparison argument. Это показано в следующем коде:These are shown in the following code:

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase);
bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase);
int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase);

Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}");
if (comparison < 0)
    Console.WriteLine($"<{root}> is less than <{root2}>");
else if (comparison > 0)
    Console.WriteLine($"<{root}> is greater than <{root2}>");
else
    Console.WriteLine($"<{root}> and <{root2}> are equivalent in order");

При выполнении порядкового сравнения без учета регистра эти методы используют соглашения о регистре для инвариантного языка и региональных параметров.When performing a case-insensitive ordinal comparison, these methods use the casing conventions of the invariant culture.

Лингвистические сравненияLinguistic comparisons

Строки могут быть упорядочены с использованием лингвистических правил для текущих значений языка и региональных параметров.Strings can also be ordered using linguistic rules for the current culture. Иногда это называется порядком сортировки слов.This is sometimes referred to as "word sort order." При лингвистическом сравнении некоторые символы Юникода, отличные от алфавитно-цифровых, могут иметь особые весовые коэффициенты.When you perform a linguistic comparison, some nonalphanumeric Unicode characters might have special weights assigned. Например, дефис "-" может иметь очень низкий весовой коэффициент, чтобы слова "co-op" и "coop" находились рядом друг с другом в порядке сортировки.For example, the hyphen "-" may have a very small weight assigned to it so that "co-op" and "coop" appear next to each other in sort order. Кроме того, некоторые символы Юникода могут быть эквивалентны последовательности экземпляров Char.In addition, some Unicode characters may be equivalent to a sequence of Char instances. В следующем примере используется фраза "Они танцуют на улице."The following example uses the phrase "They dance in the street." на немецком языке с буквами "ss" (U+0073 U+0073) в одной строке и буквой "ß" (U+00DF) в другой.in German with the "ss" (U+0073 U+0073) in one string and 'ß' (U+00DF) in another. Лингвистически (в Windows) буквы "ss" равнозначны немецкому символу эсцет "ß" в языках "en-US" и "de-DE".Linguistically (in Windows), "ss" is equal to the German Esszet: 'ß' character in both the "en-US" and "de-DE" cultures.

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

bool equal = String.Equals(first, second, StringComparison.InvariantCulture);
Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal.");
showComparison(first, second);

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words);
showComparison(word, other);
showComparison(words, other);
void showComparison(string one, string two)
{
    int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using invariant culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

Этот пример демонстрирует зависимость операций лингвистического сравнения от операционной системы.This sample demonstrates the operating system-dependent nature of linguistic comparisons. Для интерактивного окна используется узел Linux.The host for the interactive window is a Linux host. Лингвистические и порядковые сравнения дают одинаковые результаты.The linguistic and ordinal comparisons produce the same results. Если запустить этот пример на узле Windows, отображаются следующие выходные данные:If you ran this same sample on a Windows host, you would see the following output:

<coop> is less than <co-op> using invariant culture
<coop> is greater than <co-op> using ordinal comparison
<coop> is less than <cop> using invariant culture
<coop> is less than <cop> using ordinal comparison
<co-op> is less than <cop> using invariant culture
<co-op> is less than <cop> using ordinal comparison

В Windows порядок сортировки "cop", "coop" и "co-op" изменяется при переходе от лингвистического сравнения к порядковому.On Windows, the sort order of "cop", "coop", and "co-op" change when you change from a linguistic comparison to an ordinal comparison. Два предложения на немецком языке также сравниваются по-разному при использовании разных типов сравнения.The two German sentences also compare differently using the different comparison types.

Сравнения с использованием определенных языков и региональных параметровComparisons using specific cultures

Этот пример сохраняет объекты CultureInfo для языков "en-US" и "de-DE".This sample stores CultureInfo objects for the en-US and de-DE cultures. Сравнения выполняются с использованием объекта CultureInfo, чтобы учесть язык и региональные параметры.The comparisons are performed using a CultureInfo object to ensure a culture-specific comparison.

Используемые значения языка и региональных параметров влияют на операции лингвистического сравнения.The culture used affects linguistic comparisons. В следующем примере показаны результаты сравнения двух предложений на немецком с использованием языка и региональных параметров "en US" и "de-DE":The following example shows the results of comparing the two German sentences using the "en-US" culture and the "de-DE" culture:

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

var en = new System.Globalization.CultureInfo("en-US");

// For culture-sensitive comparisons, use the String.Compare 
// overload that takes a StringComparison value.
int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {en.Name} returns {i}.");

var de = new System.Globalization.CultureInfo("de-DE");
i = String.Compare(first, second, de, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {de.Name} returns {i}.");

bool b = String.Equals(first, second, StringComparison.CurrentCulture);
Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal.");

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words, en);
showComparison(word, other, en);
showComparison(words, other, en);
void showComparison(string one, string two, System.Globalization.CultureInfo culture)
{
    int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using en-US culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

Сравнения с учетом языка и региональных параметров обычно используются для сравнения и сортировки строк, вводимых пользователями.Culture-sensitive comparisons are typically used to compare and sort strings input by users with other strings input by users. Символы и правила сортировки этих строк могут различаться в зависимости от языкового стандарта компьютера пользователя.The characters and sorting conventions of these strings might vary depending on the locale of the user's computer. Даже строки, содержащие идентичные символы, могут быть отсортированы по-разному, в зависимости от языка и региональных параметров текущего потока.Even strings that contain identical characters might sort differently depending on the culture of the current thread. Кроме того, попробуйте запустить этот пример кода локально на компьютере с Windows, и вы получите следующие результаты:In addition, try this sample code locally on a Windows machine, and you will the following results:

<coop> is less than <co-op> using en-US culture
<coop> is greater than <co-op> using ordinal comparison
<coop> is less than <cop> using en-US culture
<coop> is less than <cop> using ordinal comparison
<co-op> is less than <cop> using en-US culture
<co-op> is less than <cop> using ordinal comparison

Лингвистические сравнения зависят от текущих значений языка и региональных параметров, а также от операционной системы.Linguistic comparisons are dependent on the current culture, and are OS dependent. Это необходимо учитывать при работе со строковыми сравнениями.You must take that into account when you work with string comparisons.

Лингвистическая сортировка и поиск строк в массивахLinguistic sorting and searching strings in arrays

Приведенные ниже примеры показывают, как сортировать и искать строки в массиве с помощью лингвистического сравнения, зависящего от текущих значений языка и региональных параметров.The following examples show how to sort and search for strings in an array using a linguistic comparison dependent on the current culture. Используйте статические методы Array, которые принимают параметр System.StringComparer.You use the static Array methods that take a System.StringComparer parameter.

В этом примере показано, как сортировать массив строк с использованием текущих значений языка и региональных параметров:This example shows how to sort an array of strings using the current culture:

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

// Specify Ordinal to demonstrate the different behavior.
Array.Sort(lines, StringComparer.CurrentCulture);

foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

После сортировки массива можно выполнить поиск записей с помощью двоичного поиска.Once the array is sorted, you can search for entries using a binary search. Двоичный поиск начинается с середины коллекции, чтобы определить, какая половина коллекции содержит искомую строку.A binary search starts in the middle of the collection to determine which half of the collection would contain the sought string. Каждое последующее сравнение делит оставшуюся часть коллекции пополам.Each subsequent comparison subdivides the remaining part of the collection in half. Массив сортируется с использованием StringComparer.CurrentCulture.The array is sorted using the StringComparer.CurrentCulture. Локальная функция ShowWhere отображает сведения о том, где была найдена строка.The local function ShowWhere displays information about where the string was found. Если строка не найдена, возвращаемое значение указывает, где бы оно находилось, если было бы найдено.If the string was not found, the returned value indicates where it would be if it were found.

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
Array.Sort(lines, StringComparer.CurrentCulture);

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(T[] array, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{array[index - 1]} and ");

        if (index == array.Length)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{array[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

Порядковая сортировка и поиск в коллекцияхOrdinal sorting and searching in collections

Следующий код использует класс коллекции System.Collections.Generic.List<T> для хранения строк.The following code uses the System.Collections.Generic.List<T> collection class to store strings. Строки сортируются с помощью метода List<T>.Sort.The strings are sorted using the List<T>.Sort method. Этому методу нужен делегат, который сравнивает и упорядочивает две строки.This method needs a delegate that compares and orders two strings. Метод String.CompareTo предоставляет эту функцию сравнения.The String.CompareTo method provides that comparison function. Запустите пример и следите за порядком.Run the sample and observe the order. Эта операция сортировки использует порядковую сортировку с учетом регистра.This sort operation uses an ordinal case sensitive sort. Можно использовать статические методы String.Compare, чтобы указать разные правила сравнения.You would use the static String.Compare methods to specify different comparison rules.

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

lines.Sort((left, right) => left.CompareTo(right)); 
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

После сортировки по списку строк можно осуществлять двоичный поиск.Once sorted, the list of strings can be searched using a binary search. Приведенный ниже пример описывает поиск в отсортированном списке с использованием той же функции сравнения.The following sample shows how to search the sorted listed using the same comparison function. Локальная функция ShowWhere показывает, где находится или находился бы искомый текст:The local function ShowWhere shows where the sought text is or would be:

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
lines.Sort((left, right) => left.CompareTo(right));

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = lines.BinarySearch(searchString);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(IList<T> collection, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{collection[index - 1]} and ");

        if (index == collection.Count)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{collection[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

Всегда используйте один и тот же тип сравнения для сортировки и поиска.Always make sure to use the same type of comparison for sorting and searching. Использование разных типов сравнения приводит к неожиданным результатам.Using different comparison types for sorting and searching produces unexpected results.

Классы коллекций, такие как System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue>, и System.Collections.Generic.List<T>, имеют конструкторы, принимающие параметр System.StringComparer, если типом элементов или ключей является string.Collection classes such as System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue>, and System.Collections.Generic.List<T> have constructors that take a System.StringComparer parameter when the type of the elements or keys is string. В целом, по возможности следует использовать эти конструкторы и задавать либо StringComparer.Ordinal, либо StringComparer.OrdinalIgnoreCase.In general, you should use these constructors whenever possible, and specify either StringComparer.Ordinal or StringComparer.OrdinalIgnoreCase.

Равенство ссылок и интернирование строкReference equality and string interning

Ни один из примеров не использовал ReferenceEquals.None of the samples have used ReferenceEquals. Этот метод определяет, являются ли две строки одним объектом.This method determines if two strings are the same object. Это может привести к непредсказуемым результатам при сравнении строк.This can lead to inconsistent results in string comparisons. Следующий пример демонстрирует функцию интернирования строк в C#.The following example demonstrates the string interning feature of C#. При объявлении программой двух или более идентичных переменных строк компилятор сохраняет их в одном расположении.When a program declares two or more identical string variables, the compiler stores them all in the same location. Вызвав метод ReferenceEquals, можно увидеть, что две строки фактически ссылаются на один и тот же объект в памяти.By calling the ReferenceEquals method, you can see that the two strings actually refer to the same object in memory. Чтобы избежать интернирования, используйте метод String.Copy.Use the String.Copy method to avoid interning. После копирования две строки имеют разное расположение хранения, хотя и имеют одинаковое значение.After the copy has been made, the two strings have different storage locations, even though they have the same value. Запустите следующий пример, показывающий, что строки a и b интернированы, находятся в одном хранилище.Run the following sample to show that strings a and b are interned meaning they share the same storage. Строки a и c таковыми не являются.The strings a and c are not.

string a = "The computer ate my source code.";
string b = "The computer ate my source code.";

if (String.ReferenceEquals(a, b))
    Console.WriteLine("a and b are interned.");
else
    Console.WriteLine("a and b are not interned.");

string c = String.Copy(a);

if (String.ReferenceEquals(a, c))
    Console.WriteLine("a and c are interned.");
else
    Console.WriteLine("a and c are not interned.");

Примечание

При проверке строк на равенство нужно использовать методы, которые явно указывают, какой вид сравнения следует выполнить.When you test for equality of strings, you should use the methods that explicitly specify what kind of comparison you intend to perform. Это делает код намного более понятным и удобочитаемым.Your code is much more maintainable and readable. Используйте перегрузки методов классов System.String и System.Array, которые принимают параметр перечисления StringComparison.Use the overloads of the methods of the System.String and System.Array classes that take a StringComparison enumeration parameter. Это позволяет указать тип выполняемого сравнения.You specify which type of comparison to perform. Старайтесь избегать использования операторов == и != при проверке на равенство.Avoid using the == and != operators when you test for equality. Методы экземпляра String.CompareTo всегда выполняют порядковое сравнение с учетом регистра.The String.CompareTo instance methods always perform an ordinal case-sensitive comparison. Они предназначены, прежде всего, для упорядочивания строк в алфавитном порядке.They are primarily suited for ordering strings alphabetically.

Вы можете интернировать строку или получить ссылку на существующую интернированную строку, вызвав метод String.Intern.You can intern a string or retrieve a reference to an existing interned string by calling the String.Intern method. Чтобы определить, является ли строка интернированной, вызовите метод String.IsInterned.To determine whether a string is interned, call the String.IsInterned method.

См. такжеSee also