Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
В этой статье приводятся дополнительные замечания к справочной документации по этому API.
Строка — это последовательная коллекция символов, используемых для представления текста. String Объект представляет собой последовательную коллекцию System.Char объектов, представляющих строку; System.Char объект соответствует единице кода UTF-16. Значение String объекта — это содержимое последовательной коллекции System.Char объектов, и это значение является неизменяемым (т. е. только для чтения). Дополнительные сведения о неизменяемости строк см. в разделе Неизменяемость и класс StringBuilder. Максимальный String размер объекта в памяти составляет 2 ГБ или около 1 миллиарда символов.
Дополнительные сведения об Юникоде, UTF-16, единицах кода, точках кода и CharRune типах см. в статье "Введение в кодировку символов в .NET".
Создать объект String
Создать экземпляр String объекта можно следующим образом:
Присваивая строковый литерал переменной String. Это наиболее часто используемый метод для создания строки. В следующем примере используется присваивание для создания нескольких строк. Обратите внимание на следующее: в C# и F# поскольку обратная косая черта (\) является escape-символом, литеральные обратные косые черты в строке должны быть экранированы или вся строка должна быть оформлена с @.
string string1 = "This is a string created by assignment."; Console.WriteLine(string1); string string2a = "The path is C:\\PublicDocuments\\Report1.doc"; Console.WriteLine(string2a); string string2b = @"The path is C:\PublicDocuments\Report1.doc"; Console.WriteLine(string2b); // The example displays the following output: // This is a string created by assignment. // The path is C:\PublicDocuments\Report1.doc // The path is C:\PublicDocuments\Report1.doclet string1 = "This is a string created by assignment." printfn "%s" string1 let string2a = "The path is C:\\PublicDocuments\\Report1.doc" printfn "%s" string2a let string2b = @"The path is C:\PublicDocuments\Report1.doc" printfn "%s" string2b // The example displays the following output: // This is a string created by assignment. // The path is C:\PublicDocuments\Report1.doc // The path is C:\PublicDocuments\Report1.docDim string1 As String = "This is a string created by assignment." Console.WriteLine(string1) Dim string2 As String = "The path is C:\PublicDocuments\Report1.doc" Console.WriteLine(string2) ' The example displays the following output: ' This is a string created by assignment. ' The path is C:\PublicDocuments\Report1.docПосредством вызова конструктора класса String. В следующем примере создаются экземпляры строк путем вызова нескольких конструкторов классов. Обратите внимание, что некоторые конструкторы включают указатели на символьные массивы или подписанные массивы байтов в качестве параметров. Visual Basic не поддерживает вызовы этих конструкторов. Подробные сведения о конструкторах String можно найти в сводке конструктора String.
char[] chars = { 'w', 'o', 'r', 'd' }; sbyte[] bytes = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x00 }; // Create a string from a character array. string string1 = new string(chars); Console.WriteLine(string1); // Create a string that consists of a character repeated 20 times. string string2 = new string('c', 20); Console.WriteLine(string2); string stringFromBytes = null; string stringFromChars = null; unsafe { fixed (sbyte* pbytes = bytes) { // Create a string from a pointer to a signed byte array. stringFromBytes = new string(pbytes); } fixed (char* pchars = chars) { // Create a string from a pointer to a character array. stringFromChars = new string(pchars); } } Console.WriteLine(stringFromBytes); Console.WriteLine(stringFromChars); // The example displays the following output: // word // cccccccccccccccccccc // ABCDE // wordlet chars = [| 'w'; 'o'; 'r'; 'd' |] let bytes = [| 0x41y; 0x42y; 0x43y; 0x44y; 0x45y; 0x00y |] // Create a string from a character array. let string1 = String chars printfn "%s" string1 // Create a string that consists of a character repeated 20 times. let string2 = String('c', 20) printfn "%s" string2 let stringFromBytes = // Create a string from a pointer to a signed byte array. use pbytes = fixed bytes String pbytes let stringFromChars = // Create a string from a pointer to a character array. use pchars = fixed chars String pchars printfn $"{stringFromBytes}" printfn $"{stringFromChars}" // The example displays the following output: // word // cccccccccccccccccccc // ABCDE // wordDim chars() As Char = {"w"c, "o"c, "r"c, "d"c} ' Create a string from a character array. Dim string1 As New String(chars) Console.WriteLine(string1) ' Create a string that consists of a character repeated 20 times. Dim string2 As New String("c"c, 20) Console.WriteLine(string2) ' The example displays the following output: ' word ' ccccccccccccccccccccИспользуя оператор объединения строк (+ в C# и F#, & или + в Visual Basic) для создания единой строки из сочетания любых
элементов и строковых литералов. В следующем примере показано использование оператора объединения строк. string string1 = "Today is " + DateTime.Now.ToString("D") + "."; Console.WriteLine(string1); string string2 = "This is one sentence. " + "This is a second. "; string2 += "This is a third sentence."; Console.WriteLine(string2); // The example displays output like the following: // Today is Tuesday, July 06, 2011. // This is one sentence. This is a second. This is a third sentence.let string1 = "Today is " + DateTime.Now.ToString("D") + "." printfn $"{string1}" let string2 = "This is one sentence. " + "This is a second. " let string2 = string2 + "This is a third sentence." printfn $"{string2}" // The example displays output like the following: // Today is Tuesday, July 06, 2011. // This is one sentence. This is a second. This is a third sentence.Dim string1 As String = "Today is " + Date.Now.ToString("D") + "." Console.WriteLine(string1) Dim string2 As String = "This is one sentence. " + "This is a second. " string2 += "This is a third sentence." Console.WriteLine(string2) ' The example displays output like the following: ' Today is Tuesday, July 06, 2011. ' This is one sentence. This is a second. This is a third sentence.При получении значения свойства или вызове метода, возвращающего строку. В следующем примере методы String класса используются для извлечения подстроки из более крупной строки.
string sentence = "This sentence has five words."; // Extract the second word. int startPosition = sentence.IndexOf(" ") + 1; string word2 = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition); Console.WriteLine("Second word: " + word2); // The example displays the following output: // Second word: sentencelet sentence = "This sentence has five words." // Extract the second word. let startPosition = sentence.IndexOf " " + 1 let word2 = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition) printfn $"Second word: {word2}" // The example displays the following output: // Second word: sentenceDim sentence As String = "This sentence has five words." ' Extract the second word. Dim startPosition As Integer = sentence.IndexOf(" ") + 1 Dim word2 As String = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition) Console.WriteLine("Second word: " + word2) ' The example displays the following output: ' Second word: sentenceПутем вызова метода форматирования для преобразования значения или объекта в его строковое представление. В следующем примере используется компонент составного форматирования для внедрения строкового представления двух объектов в строку.
DateTime dateAndTime = new DateTime(2011, 7, 6, 7, 32, 0); double temperature = 68.3; string result = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature); Console.WriteLine(result); // The example displays the following output: // At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.let dateAndTime = DateTime(2011, 7, 6, 7, 32, 0) let temperature = 68.3 String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature) |> printfn "%s" // The example displays the following output: // At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.Dim dateAndTime As DateTime = #07/06/2011 7:32:00AM# Dim temperature As Double = 68.3 Dim result As String = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature) Console.WriteLine(result) ' The example displays the following output: ' At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.
Объекты Char и символы Юникода
Каждый символ в строке определяется скалярным значением Юникода, также называемым точкой кода Юникода или порядковым (числовым) значением символа Юникода. Каждая точка кода кодируется с помощью кодировки UTF-16, а числовое значение каждого элемента кодирования представлено Char объектом.
Примечание.
Обратите внимание, что, поскольку String экземпляр состоит из последовательной коллекции единиц кода UTF-16, можно создать String объект, который не является хорошо сформированной строкой Юникода. Например, можно создать строку с низким суррогатным кодом без соответствующего высокого суррогатного кода. Хотя некоторые методы, такие как методы кодирования и декодирования объектов в System.Text пространстве имен, могут выполнять проверки, чтобы убедиться, что строки хорошо сформированы, String члены класса не гарантируют правильность формирования строки.
Один Char объект обычно представляет одну кодовую точку, то есть числовое значение Char точки кода равно кодовой точке. Например, кодовая точка для символа "a" — U+0061. Однако для точки кода может потребоваться несколько закодированных элементов (более одного Char объекта). Стандарт Юникода определяет два типа символов, которые соответствуют нескольким Char объектам: графемы и дополнительные точки кода Юникода, соответствующие символам в дополнительных плоскостях Юникода.
Графем представлен базовым символом, за которым следует один или несколько объединенных символов. Например, символ ä представлен Char объектом, кодовая точка которого — U+0061, за которой следует Char объект, кодовая точка которого — U+0308. Этот символ также можно определить одним Char объектом, который имеет кодовую точку U+00E4. Как показано в следующем примере, сравнение с учётом культурных особенностей указывает, что эти два представления равны, хотя обычное порядковое сравнение этого не делает. Однако если две строки нормализуются, порядковое сравнение также указывает, что они равны. (Дополнительные сведения о нормализации строк см. в разделе Нормализация.)
using System; using System.Globalization; using System.IO; public class Example5 { public static void Main() { StreamWriter sw = new StreamWriter(@".\graphemes.txt"); string grapheme = "\u0061\u0308"; sw.WriteLine(grapheme); string singleChar = "\u00e4"; sw.WriteLine(singleChar); sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)); sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)); sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)); sw.Close(); } } // The example produces the following output: // ä // ä // ä = ä (Culture-sensitive): True // ä = ä (Ordinal): False // ä = ä (Normalized Ordinal): Trueopen System open System.IO do use sw = new StreamWriter(@".\graphemes.txt") let grapheme = "\u0061\u0308" sw.WriteLine grapheme let singleChar = "\u00e4" sw.WriteLine singleChar sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)) sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)) sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)) // The example produces the following output: // ä // ä // ä = ä (Culture-sensitive): True // ä = ä (Ordinal): False // ä = ä (Normalized Ordinal): TrueImports System.Globalization Imports System.IO Module Example9 Public Sub Main() Dim sw As New StreamWriter(".\graphemes.txt") Dim grapheme As String = ChrW(&H61) + ChrW(&H308) sw.WriteLine(grapheme) Dim singleChar As String = ChrW(&HE4) sw.WriteLine(singleChar) sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)) sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)) sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)) sw.Close() End Sub End Module ' The example produces the following output: ' ä ' ä ' ä = ä (Culture-sensitive): True ' ä = ä (Ordinal): False ' ä = ä (Normalized Ordinal): TrueДополнительная кодовая точка Юникода (суррогатная пара) представлена Char объектом, кодовая точка которого является высокой суррогатной, за которой следует Char объект, кодовая точка которого является низкой суррогатной. Единицы кода с высоким уровнем суррогатов варьируются от U+D800 до U+DBFF. Единицы кода низких суррогатов находятся в диапазоне от U+DC00 до U+DFFF. Суррогатные пары используются для представления символов в 16 дополнительных плоскостях Юникода. В следующем примере создается суррогатный символ и передается методу Char.IsSurrogatePair(Char, Char), чтобы определить, является ли он частью суррогатной пары.
string surrogate = "\uD800\uDC03"; for (int ctr = 0; ctr < surrogate.Length; ctr++) Console.Write($"U+{(ushort)surrogate[ctr]:X2} "); Console.WriteLine(); Console.WriteLine($" Is Surrogate Pair: {Char.IsSurrogatePair(surrogate[0], surrogate[1])}"); // The example displays the following output: // U+D800 U+DC03 // Is Surrogate Pair: Trueopen System let surrogate = "\uD800\uDC03" for i = 0 to surrogate.Length - 1 do printf $"U+{uint16 surrogate[i]:X2} " printfn $"\n Is Surrogate Pair: {Char.IsSurrogatePair(surrogate[0], surrogate[1])}" // The example displays the following output: // U+D800 U+DC03 // Is Surrogate Pair: TrueModule Example20 Public Sub Main() Dim surrogate As String = ChrW(&HD800) + ChrW(&HDC03) For ctr As Integer = 0 To surrogate.Length - 1 Console.Write("U+{0:X2} ", Convert.ToUInt16(surrogate(ctr))) Next Console.WriteLine() Console.WriteLine(" Is Surrogate Pair: {0}", Char.IsSurrogatePair(surrogate(0), surrogate(1))) End Sub End Module ' The example displays the following output: ' U+D800 U+DC03 ' Is Surrogate Pair: True
Стандарт Юникода
Символы в строке представлены единицами кода в кодировке UTF-16, которые соответствуют Char значениям.
Каждый символ в строке имеет связанную категорию символов Юникода, которая представлена в .NET UnicodeCategory перечислением. Категорию символа или суррогатной пары можно определить путем вызова CharUnicodeInfo.GetUnicodeCategory метода.
.NET поддерживает собственную таблицу символов с соответствующими категориями, которая гарантирует, что конкретные реализации .NET будут возвращать одни и те же данные по категориям символов на разных платформах. Во всех версиях .NET и на всех платформах ОС сведения о категории символов предоставляются базой данных символов Юникода.
В следующей таблице перечислены версии .NET и версии стандарта Юникод, на котором основаны их категории символов.
| Версия .NET | Версия стандарта Юникод |
|---|---|
| .NET Framework 1.1 | Стандарт Юникод, версия 4.0.0 |
| .NET Framework 2.0 | Стандарт Юникод, версия 5.0.0 |
| .NET Framework 3.5 | Стандарт Юникод, версия 5.0.0 |
| платформа .NET Framework 4 | Стандарт Юникод, версия 5.0.0 |
| .NET Framework 4.5 | Стандарт Юникод, версия 6.3.0 |
| .NET Framework 4.5.1 | Стандарт Юникод, версия 6.3.0 |
| .NET Framework 4.5.2 | Стандарт Юникод, версия 6.3.0 |
| .NET Framework 4.6 | Стандарт Юникод, версия 6.3.0 |
| .NET Framework 4.6.1 | Стандарт Юникод, версия 6.3.0 |
| .NET Framework 4.6.2 и более поздние версии | Стандарт Юникод, версия 8.0.0 |
| .NET Core 2.1 | Стандарт Юникод, версия 8.0.0 |
| .NET Core 3.1. | Стандарт Юникода версии 11.0.0 |
| .NET 5 | Стандарт Юникода версии 13.0.0 |
Кроме того, .NET поддерживает сравнение строк и сортировку на основе стандарта Юникода. Начиная с платформы .NET Framework 4.5, работающей на Windows 8 и более поздних версиях операционной системы Windows, среда выполнения делегирует операции сравнения и сортировки строк операционной системе. В .NET Core и .NET 5+ сведения о сравнении строк и сортировке предоставляются международными компонентами библиотек Юникода (за исключением версий Windows до обновление Windows 10 за май 2019 г.). В следующей таблице перечислены версии .NET и версии Юникода Standard, на основе которых основаны сравнение символов и сортировка.
| Версия .NET | Версия стандарта Юникод |
|---|---|
| .NET Framework 4.5 и более поздних версий в Windows 7 | Стандарт Юникод, версия 5.0.0 |
| на платформе .NET Framework 4.5 и более поздних версиях на операционных системах Windows 8 и более поздних версиях | Стандарт Юникод, версия 6.3.0 |
| .NET Core и .NET 5 или более поздней версии | Зависит от версии стандарта Юникода, поддерживаемой базовой операционной системой. |
Внедренные нулевые символы
В .NET String объект может включать внедренные пустые символы, которые считаются частью длины строки. Однако на некоторых языках, таких как C и C++, символ NULL указывает конец строки; он не считается частью строки и не считается частью длины строки. Это означает, что следующие распространенные предположения о строках, которые могут делать программисты C и C++ или библиотеки, написанные на C или C++, не обязательно действительны, когда применяются к String объектам.
Возвращаемое значение функциями
strlenилиwcslenне обязательно равно String.Length.Строка, созданная функциями
strcpy_sилиwcscpy_s, не обязательно идентична копируемой строке.
Необходимо убедиться, что собственный код C и C++, который создает экземпляры String объектов, и код, которому передаются String объекты через вызов платформы, не предполагает, что встроенный нуль-символ обозначает конец строки.
Внедренные символы NULL в строке также обрабатываются по-разному при сортировке строки (или сравнении) и при поиске строки. Нулевые символы игнорируются при выполнении сравнения с учетом языка и региональных параметров между двумя строками, включая сравнения с использованием инвариантной культуры. Они считаются только для порядковых или нечувствительных порядковых сравнений. С другой стороны, внедренные символы NULL всегда учитываются при поиске строки с помощью таких методов, как Contains, StartsWithи IndexOf.
Строки и индексы
Индекс — это позиция Char объекта (а не символа Юникода) в объекте String. Индекс — это отсчитываемое от нуля число, начинающееся с первой позиции в строке, которая является нулевой позицией индекса. Ряд методов поиска, таких как IndexOf и LastIndexOf, возвращает индекс символа или подстроки в экземпляре строки.
Свойство Chars[Int32] позволяет получить доступ к отдельным Char объектам по их позиции индекса в строке. Chars[Int32] Так как свойство является свойством по умолчанию (в Visual Basic) или индексатором (в C# и F#), вы можете получить доступ к отдельным Char объектам в строке с помощью кода, например следующего. Этот код ищет пробелы или знаки препинания в строке, чтобы определить, сколько слов содержит строка.
string s1 = "This string consists of a single short sentence.";
int nWords = 0;
s1 = s1.Trim();
for (int ctr = 0; ctr < s1.Length; ctr++) {
if (Char.IsPunctuation(s1[ctr]) | Char.IsWhiteSpace(s1[ctr]))
nWords++;
}
Console.WriteLine($"""
The sentence
{s1}
has {nWords} words.
""");
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.
let s1 = "This string consists of a single short sentence."
let mutable nWords = 0
for i = 0 to s1.Length - 1 do
if Char.IsPunctuation s1[i] || Char.IsWhiteSpace s1[i] then
nWords <- nWords + 1
printfn $"The sentence\n {s1}\nhas {nWords} words."
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.
Module Example12
Public Sub Main()
Dim s1 As String = "This string consists of a single short sentence."
Dim nWords As Integer = 0
s1 = s1.Trim()
For ctr As Integer = 0 To s1.Length - 1
If Char.IsPunctuation(s1(ctr)) Or Char.IsWhiteSpace(s1(ctr)) Then
nWords += 1
End If
Next
Console.WriteLine("The sentence{2} {0}{2}has {1} words.",
s1, nWords, vbCrLf)
End Sub
End Module
' The example displays the following output:
' The sentence
' This string consists of a single short sentence.
' has 8 words.
Поскольку класс String реализует интерфейс IEnumerable, можно также перебирать объекты Char в строке с помощью конструкции foreach, как показано в следующем примере.
string s1 = "This string consists of a single short sentence.";
int nWords = 0;
s1 = s1.Trim();
foreach (var ch in s1) {
if (Char.IsPunctuation(ch) | Char.IsWhiteSpace(ch))
nWords++;
}
Console.WriteLine($"""
The sentence
{s1}
has {nWords} words.
""");
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.
let s1 = "This string consists of a single short sentence."
let mutable nWords = 0
for ch in s1 do
if Char.IsPunctuation ch || Char.IsWhiteSpace ch then
nWords <- nWords + 1
printfn $"The sentence\n {s1}\nhas {nWords} words."
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.
Module Example13
Public Sub Main()
Dim s1 As String = "This string consists of a single short sentence."
Dim nWords As Integer = 0
s1 = s1.Trim()
For Each ch In s1
If Char.IsPunctuation(ch) Or Char.IsWhiteSpace(ch) Then
nWords += 1
End If
Next
Console.WriteLine("The sentence{2} {0}{2}has {1} words.",
s1, nWords, vbCrLf)
End Sub
End Module
' The example displays the following output:
' The sentence
' This string consists of a single short sentence.
' has 8 words.
Последовательные значения индекса могут не соответствовать последовательными символами Юникода, так как символ Юникода может быть закодирован как несколько Char объектов. В частности, строка может содержать многозначные единицы текста, которые формируются базовым символом, за которым следует один или несколько объединенных символов или суррогатных пар. Для работы с символами Юникода вместо объектов Char, используйте классы System.Globalization.StringInfo и TextElementEnumerator, или метод String.EnumerateRunes и структуру Rune. В следующем примере показано различие между кодом, который работает с объектами, и кодом, который работает с символами Юникода Char. Он сравнивает количество символов или текстовых элементов в каждом слове предложения. Строка содержит две последовательности базового символа, за которым следует объединение символов.
// First sentence of The Mystery of the Yellow Room, by Leroux.
string opening = "Ce n'est pas sans une certaine émotion que "+
"je commence à raconter ici les aventures " +
"extraordinaires de Joseph Rouletabille.";
// Character counters.
int nChars = 0;
// Objects to store word count.
List<int> chars = new List<int>();
List<int> elements = new List<int>();
foreach (var ch in opening) {
// Skip the ' character.
if (ch == '\u0027') continue;
if (Char.IsWhiteSpace(ch) | (Char.IsPunctuation(ch))) {
chars.Add(nChars);
nChars = 0;
}
else {
nChars++;
}
}
System.Globalization.TextElementEnumerator te =
System.Globalization.StringInfo.GetTextElementEnumerator(opening);
while (te.MoveNext()) {
string s = te.GetTextElement();
// Skip the ' character.
if (s == "\u0027") continue;
if ( String.IsNullOrEmpty(s.Trim()) | (s.Length == 1 && Char.IsPunctuation(Convert.ToChar(s)))) {
elements.Add(nChars);
nChars = 0;
}
else {
nChars++;
}
}
// Display character counts.
Console.WriteLine("{0,6} {1,20} {2,20}",
"Word #", "Char Objects", "Characters");
for (int ctr = 0; ctr < chars.Count; ctr++)
Console.WriteLine("{0,6} {1,20} {2,20}",
ctr, chars[ctr], elements[ctr]);
// The example displays the following output:
// Word # Char Objects Characters
// 0 2 2
// 1 4 4
// 2 3 3
// 3 4 4
// 4 3 3
// 5 8 8
// 6 8 7
// 7 3 3
// 8 2 2
// 9 8 8
// 10 2 1
// 11 8 8
// 12 3 3
// 13 3 3
// 14 9 9
// 15 15 15
// 16 2 2
// 17 6 6
// 18 12 12
open System
open System.Globalization
// First sentence of The Mystery of the Yellow Room, by Leroux.
let opening = "Ce n'est pas sans une certaine émotion que je commence à raconter ici les aventures extraordinaires de Joseph Rouletabille."
// Character counters.
let mutable nChars = 0
// Objects to store word count.
let chars = ResizeArray<int>()
let elements = ResizeArray<int>()
for ch in opening do
// Skip the ' character.
if ch <> '\u0027' then
if Char.IsWhiteSpace ch || Char.IsPunctuation ch then
chars.Add nChars
nChars <- 0
else
nChars <- nChars + 1
let te = StringInfo.GetTextElementEnumerator opening
while te.MoveNext() do
let s = te.GetTextElement()
// Skip the ' character.
if s <> "\u0027" then
if String.IsNullOrEmpty(s.Trim()) || (s.Length = 1 && Char.IsPunctuation(Convert.ToChar s)) then
elements.Add nChars
nChars <- 0
else
nChars <- nChars + 1
// Display character counts.
printfn "%6s %20s %20s" "Word #" "Char Objects " "Characters"
for i = 0 to chars.Count - 1 do
printfn "%6d %20d %20d" i chars[i] elements[i]
// The example displays the following output:
// Word # Char Objects Characters
// 0 2 2
// 1 4 4
// 2 3 3
// 3 4 4
// 4 3 3
// 5 8 8
// 6 8 7
// 7 3 3
// 8 2 2
// 9 8 8
// 10 2 1
// 11 8 8
// 12 3 3
// 13 3 3
// 14 9 9
// 15 15 15
// 16 2 2
// 17 6 6
// 18 12 12
Imports System.Collections.Generic
Imports System.Globalization
Module Example14
Public Sub Main()
' First sentence of The Mystery of the Yellow Room, by Leroux.
Dim opening As String = "Ce n'est pas sans une certaine émotion que " +
"je commence à raconter ici les aventures " +
"extraordinaires de Joseph Rouletabille."
' Character counters.
Dim nChars As Integer = 0
' Objects to store word count.
Dim chars As New List(Of Integer)()
Dim elements As New List(Of Integer)()
For Each ch In opening
' Skip the ' character.
If ch = ChrW(&H27) Then Continue For
If Char.IsWhiteSpace(ch) Or Char.IsPunctuation(ch) Then
chars.Add(nChars)
nChars = 0
Else
nChars += 1
End If
Next
Dim te As TextElementEnumerator = StringInfo.GetTextElementEnumerator(opening)
Do While te.MoveNext()
Dim s As String = te.GetTextElement()
' Skip the ' character.
If s = ChrW(&H27) Then Continue Do
If String.IsNullOrEmpty(s.Trim()) Or (s.Length = 1 AndAlso Char.IsPunctuation(Convert.ToChar(s))) Then
elements.Add(nChars)
nChars = 0
Else
nChars += 1
End If
Loop
' Display character counts.
Console.WriteLine("{0,6} {1,20} {2,20}",
"Word #", "Char Objects", "Characters")
For ctr As Integer = 0 To chars.Count - 1
Console.WriteLine("{0,6} {1,20} {2,20}",
ctr, chars(ctr), elements(ctr))
Next
End Sub
End Module
' The example displays the following output:
' Word # Char Objects Characters
' 0 2 2
' 1 4 4
' 2 3 3
' 3 4 4
' 4 3 3
' 5 8 8
' 6 8 7
' 7 3 3
' 8 2 2
' 9 8 8
' 10 2 1
' 11 8 8
' 12 3 3
' 13 3 3
' 14 9 9
' 15 15 15
' 16 2 2
' 17 6 6
' 18 12 12
Этот пример работает с текстовыми элементами с помощью StringInfo.GetTextElementEnumerator метода и TextElementEnumerator класса для перечисления всех текстовых элементов в строке. Можно также получить массив, содержащий начальный индекс каждого текстового элемента, вызвав StringInfo.ParseCombiningCharacters метод.
Дополнительные сведения о работе с единицами текста, а не отдельными Char значениями, см. в разделе "Введение в кодировку символов" в .NET.
Строки NULL и пустые строки
Строка, которая была объявлена, но не получила значения, имеет значение null. Попытка вызова методов в этой строке NullReferenceExceptionвызывает исключение. Строка NULL отличается от пустой строки, которая является строкой, значение которой имеет значение "" или String.Empty. В некоторых случаях передача пустой строки или пустой строки в качестве аргумента в вызове метода вызывает исключение. Например, передача строки null методу Int32.Parse вызывает исключение ArgumentNullException, а передача пустой строки вызывает исключение FormatException. В других случаях аргументом метода может быть либо null строка, либо пустая строка. Например, если вы предоставляете IFormattable реализацию для класса, необходимо приравнивать как строку NULL, так и пустую строку с описателями общего формата (G).
Класс String включает в себя следующие два удобных метода, которые позволяют проверить, является null ли строка или пуста:
IsNullOrEmpty, который указывает, является ли строка
nullили равна String.Empty. Этот метод устраняет необходимость использования кода, например следующего:if (str == null || str.Equals(String.Empty))if str = null || str.Equals String.Empty thenIf str Is Nothing OrElse str.Equals(String.Empty) ThenIsNullOrWhiteSpace, указывающий, является
nullли строка равной String.Emptyили состоит исключительно из символов пробелов. Этот метод устраняет необходимость использования кода, например следующего:if (str == null || str.Equals(String.Empty) || str.Trim().Equals(String.Empty))if str = null || str.Equals String.Empty || str.Trim().Equals String.Empty thenIf str Is Nothing OrElse str.Equals(String.Empty) OrElse str.Trim().Equals(String.Empty) Then
В следующем примере используется метод IsNullOrEmpty в реализации IFormattable.ToString пользовательского класса Temperature. Метод поддерживает строки формата "G", "C", "F" и "K". Если пустая строка формата или строка формата, значение null которой передается методу, его значение изменяется на строку формата G.
public string ToString(string format, IFormatProvider provider)
{
if (String.IsNullOrEmpty(format)) format = "G";
if (provider == null) provider = CultureInfo.CurrentCulture;
switch (format.ToUpperInvariant())
{
// Return degrees in Celsius.
case "G":
case "C":
return temp.ToString("F2", provider) + "°C";
// Return degrees in Fahrenheit.
case "F":
return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F";
// Return degrees in Kelvin.
case "K":
return (temp + 273.15).ToString();
default:
throw new FormatException(
String.Format("The {0} format string is not supported.",
format));
}
}
member _.ToString(format: string, provider: IFormatProvider) =
let format =
if String.IsNullOrEmpty format then "G" else format
let provider: IFormatProvider =
if provider = null then CultureInfo.CurrentCulture else provider
match format.ToUpperInvariant() with
// Return degrees in Celsius.
| "G"
| "C" ->
temp.ToString("F2", provider) + "°C"
// Return degrees in Fahrenheit.
| "F" ->
(temp * 9. / 5. + 32.).ToString("F2", provider) + "°F"
// Return degrees in Kelvin.
| "K" ->
(temp + 273.15).ToString()
| _ ->
raise (FormatException(String.Format("The {0} format string is not supported.",format)))
Public Overloads Function ToString(fmt As String, provider As IFormatProvider) As String _
Implements IFormattable.ToString
If String.IsNullOrEmpty(fmt) Then fmt = "G"
If provider Is Nothing Then provider = CultureInfo.CurrentCulture
Select Case fmt.ToUpperInvariant()
' Return degrees in Celsius.
Case "G", "C"
Return temp.ToString("F2", provider) + "°C"
' Return degrees in Fahrenheit.
Case "F"
Return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F"
' Return degrees in Kelvin.
Case "K"
Return (temp + 273.15).ToString()
Case Else
Throw New FormatException(
String.Format("The {0} format string is not supported.",
fmt))
End Select
End Function
Неизменяемость и класс StringBuilder
String Объект называется неизменяемым (только для чтения), так как его значение невозможно изменить после его создания. Методы, которые выглядят как модифицирующие объект String, на самом деле возвращают новый объект String, который содержит внесённые изменения.
Так как строки являются неизменяемыми, процедуры обработки строк, выполняющие повторяющиеся добавления или удаления в одну строку, могут вызвать значительное снижение производительности. Например, следующий код использует генератор случайных чисел для создания строки с 1000 символами в диапазоне 0x0001 для 0x052F. Хотя код, как представляется, использует объединение строк для добавления нового символа в существующую строку с именем str, он фактически создает новый String объект для каждой операции объединения.
using System;
using System.IO;
using System.Text;
public class Example6
{
public static void Main()
{
Random rnd = new Random();
string str = String.Empty;
StreamWriter sw = new StreamWriter(@".\StringFile.txt",
false, Encoding.Unicode);
for (int ctr = 0; ctr <= 1000; ctr++) {
str += (char)rnd.Next(1, 0x0530);
if (str.Length % 60 == 0)
str += Environment.NewLine;
}
sw.Write(str);
sw.Close();
}
}
open System
open System.IO
open System.Text
do
let rnd = Random()
let mutable str = String.Empty
use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode)
for _ = 0 to 1000 do
str <- str + (rnd.Next(1, 0x0530) |> char |> string)
if str.Length % 60 = 0 then
str <- str + Environment.NewLine
sw.Write str
Imports System.IO
Imports System.Text
Module Example10
Public Sub Main()
Dim rnd As New Random()
Dim str As String = String.Empty
Dim sw As New StreamWriter(".\StringFile.txt",
False, Encoding.Unicode)
For ctr As Integer = 0 To 1000
str += ChrW(rnd.Next(1, &H530))
If str.Length Mod 60 = 0 Then str += vbCrLf
Next
sw.Write(str)
sw.Close()
End Sub
End Module
Вы можете использовать класс StringBuilder вместо класса String для операций, которые вносят несколько изменений в значение строки. В отличие от экземпляров String класса, StringBuilder объекты изменяются; при объединениях, добавлении или удалении подстроок из строки операции выполняются в одной строке. Завершив изменение значения StringBuilder объекта, можно вызвать его StringBuilder.ToString метод, чтобы преобразовать его в строку. В следующем примере объект String, использованный в предыдущем примере для объединения 1000 случайных символов в диапазоне от 0x0001 до 0x052F, заменяется на объект StringBuilder.
using System;
using System.IO;
using System.Text;
public class Example10
{
public static void Main()
{
Random rnd = new Random();
StringBuilder sb = new StringBuilder();
StreamWriter sw = new StreamWriter(@".\StringFile.txt",
false, Encoding.Unicode);
for (int ctr = 0; ctr <= 1000; ctr++) {
sb.Append((char)rnd.Next(1, 0x0530));
if (sb.Length % 60 == 0)
sb.AppendLine();
}
sw.Write(sb.ToString());
sw.Close();
}
}
open System
open System.IO
open System.Text
do
let rnd = Random()
let sb = StringBuilder()
use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode)
for _ = 0 to 1000 do
sb.Append(rnd.Next(1, 0x0530) |> char) |> ignore
if sb.Length % 60 = 0 then
sb.AppendLine() |> ignore
sw.Write(string sb)
Imports System.IO
Imports System.Text
Module Example11
Public Sub Main()
Dim rnd As New Random()
Dim sb As New StringBuilder()
Dim sw As New StreamWriter(".\StringFile.txt",
False, Encoding.Unicode)
For ctr As Integer = 0 To 1000
sb.Append(ChrW(rnd.Next(1, &H530)))
If sb.Length Mod 60 = 0 Then sb.AppendLine()
Next
sw.Write(sb.ToString())
sw.Close()
End Sub
End Module
Порядковые операции с учетом языка и региональных параметров
String Участники класса выполняют порядковые или лингвистические операции над объектом String. Порядковая операция действует на числовое значение каждого Char объекта. Операция, учитывающая языковые и региональные параметры, оказывает влияние на значение объекта String, принимая во внимание культурозависимые факторы, такие как регистр, сортировка, форматирование и разбор. Операции с учетом языка и региональных параметров выполняются в контексте явно объявленного языка и региональных параметров или неявного текущего языка и региональных параметров. Два типа операций могут создавать очень разные результаты, когда они выполняются в одной строке.
.NET также поддерживает лингвистические операции с независимыми от культуры строками с помощью инвариантной культуры (CultureInfo.InvariantCulture), которая слабо основана на языковых параметрах английского языка независимо от региона. В отличие от других System.Globalization.CultureInfo параметров, параметры инвариантной культуры гарантированно остаются согласованными на одном компьютере, между системами и в разных версиях .NET. Инвариантная культура может рассматриваться как своего рода черный ящик, который обеспечивает стабильность сравнений строк и их упорядочения во всех культурах.
Внимание
Если приложение принимает решение о безопасности символьного идентификатора, например имени файла или именованного канала, или о сохраненных данных, таких как текстовые данные в XML-файле, операция должна использовать порядковое сравнение вместо сравнения с учетом языка и региональных параметров. Это связано с тем, что сравнение с учетом языка и региональных параметров может дать различные результаты в зависимости от фактических региональных параметров, в то время как порядковое сравнение зависит исключительно от двоичного значения сравниваемых символов.
Внимание
Большинство методов, выполняющих строковые операции, перегружены параметром типа StringComparison, что позволяет указать, выполняет ли метод порядковую или чувствительную к культуре операцию. Как правило, необходимо вызвать эту перегрузку, чтобы сделать намерение вызова метода ясным. Рекомендации по использованию операций с порядками и чувствительных к культуре операций со строками см. в Рекомендациях по использованию строк.
Операции изменения регистра, разбора и форматирования, сравнения и сортировки, а также проверки на равенство могут учитывать порядок или быть чувствительными к культуре. В следующих разделах рассматриваются все категории операций.
Совет
Всегда следует вызывать перегрузку метода, которая делает намерение вызова метода понятным. Например, вместо вызова метода Compare(String, String) для сравнения двух строк с учётом настроек текущей культуры, следует вызвать метод Compare(String, String, StringComparison) со значением StringComparison.CurrentCulture для аргумента comparisonType. Дополнительные сведения см. в разделе Рекомендации по использованию строк.
Вы можете скачать таблицы веса сортировки, представляющие собой набор текстовых файлов, которые содержат сведения о весах символов, используемых в операциях сортировки и сравнения, по следующим ссылкам:
- Windows (платформа .NET Framework и .NET Core): сортировка таблиц веса
- Обновление Windows 10 от мая 2019 года или более поздней версии (.NET 5+) и Linux и macOS (.NET Core и .NET 5+): Таблица элементов сортировки по умолчанию для Unicode
Корпус
Правила регистра определяют, как изменить заглавную букву символа Юникода; например, из нижнего регистра в верхний регистр. Часто изменение регистра выполняется перед сравнением строк. Например, строку можно преобразовать в верхний регистр, чтобы ее можно было сравнить с другой строкой верхнего регистра. Символы в строке можно преобразовать в строчные, вызвав метод ToLower или ToLowerInvariant, и их можно преобразовать в заглавные, вызвав метод ToUpper или ToUpperInvariant. Кроме того, можно использовать TextInfo.ToTitleCase метод для преобразования строки в регистр заголовка.
Примечание.
.NET Core, работающий только в системах Linux и macOS: поведение сортировки для региональных параметров C и Posix всегда учитывает регистр, так как эти региональные параметры не используют ожидаемого порядка сортировки Юникода. Рекомендуем использовать культуру, отличную от C или Posix, для выполнения операций сортировки с учетом языка и региональных параметров, но без учета регистра.
Операции с регистром могут быть основаны на правилах текущей культуры, заданной культуры или инвариантной культуры. Так как сопоставления регистров могут отличаться в зависимости от используемых региональных и языковых параметров, результат операций с регистром может варьироваться в зависимости от этих параметров. Фактические различия в регистре имеют три вида:
Различия в сопоставлении ЛАТИНСКОЙ ЗАГЛАВНОЙ БУКВЫ I (U+0049), ЛАТИНСКОЙ СТРОЧНОЙ БУКВЫ I (U+0069), ЛАТИНСКОЙ ЗАГЛАВНОЙ БУКВЫ С ТОЧКОЙ НАД I (U+0130) и ЛАТИНСКОЙ СТРОЧНОЙ БУКВЫ БЕЗ ТОЧКИ I (U+0131). В культурах tr-TR (турецкая (Турция)) и az-Latn-AZ (Азербайджан, латиница), а также в нейтральных культурах tr, az и az-Latn, строчный эквивалент LATIN CAPITAL LETTER I — LATIN SMALL LETTER DOTLESS I, а заглавный эквивалент LATIN SMALL LETTER I — ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА I С ТОЧКОЙ. Во всех других культурах, включая инвариантную культуру, латинская строчная буква I и латинская заглавная буква I являются строчными и прописными эквивалентами.
В следующем примере показано, как сравнение строк, предназначенное для предотвращения доступа к файловой системе, может завершиться ошибкой, если она зависит от сравнения регистра с учетом языка и региональных параметров. Должны использоваться соглашения о регистре инвариантной культуры.
using System; using System.Globalization; using System.Threading; public class Example1 { const string disallowed = "file"; public static void Main() { IsAccessAllowed(@"FILE:\\\c:\users\user001\documents\FinancialInfo.txt"); } private static void IsAccessAllowed(String resource) { CultureInfo[] cultures = { CultureInfo.CreateSpecificCulture("en-US"), CultureInfo.CreateSpecificCulture("tr-TR") }; String scheme = null; int index = resource.IndexOfAny( new Char[] { '\\', '/' } ); if (index > 0) scheme = resource.Substring(0, index - 1); // Change the current culture and perform the comparison. foreach (var culture in cultures) { Thread.CurrentThread.CurrentCulture = culture; Console.WriteLine($"Culture: {CultureInfo.CurrentCulture.DisplayName}"); Console.WriteLine(resource); Console.WriteLine($"Access allowed: {! String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase)}"); Console.WriteLine(); } } } // The example displays the following output: // Culture: English (United States) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: False // // Culture: Turkish (Turkey) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: Trueopen System open System.Globalization open System.Threading let disallowed = "file" let isAccessAllowed (resource: string) = let cultures = [| CultureInfo.CreateSpecificCulture "en-US" CultureInfo.CreateSpecificCulture "tr-TR" |] let index = resource.IndexOfAny [| '\\'; '/' |] let scheme = if index > 0 then resource.Substring(0, index - 1) else null // Change the current culture and perform the comparison. for culture in cultures do Thread.CurrentThread.CurrentCulture <- culture printfn $"Culture: {CultureInfo.CurrentCulture.DisplayName}" printfn $"{resource}" printfn $"Access allowed: {String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase) |> not}" printfn "" isAccessAllowed @"FILE:\\\c:\users\user001\documents\FinancialInfo.txt" // The example displays the following output: // Culture: English (United States) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: False // // Culture: Turkish (Turkey) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: TrueImports System.Globalization Imports System.Threading Module Example2 Const disallowed = "file" Public Sub Main() IsAccessAllowed("FILE:\\\c:\users\user001\documents\FinancialInfo.txt") End Sub Private Sub IsAccessAllowed(resource As String) Dim cultures() As CultureInfo = {CultureInfo.CreateSpecificCulture("en-US"), CultureInfo.CreateSpecificCulture("tr-TR")} Dim scheme As String = Nothing Dim index As Integer = resource.IndexOfAny({"\"c, "/"c}) If index > 0 Then scheme = resource.Substring(0, index - 1) ' Change the current culture and perform the comparison. For Each culture In cultures Thread.CurrentThread.CurrentCulture = culture Console.WriteLine("Culture: {0}", CultureInfo.CurrentCulture.DisplayName) Console.WriteLine(resource) Console.WriteLine("Access allowed: {0}", Not String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase)) Console.WriteLine() Next End Sub End Module ' The example displays the following output: ' Culture: English (United States) ' FILE:\\\c:\users\user001\documents\FinancialInfo.txt ' Access allowed: False ' ' Culture: Turkish (Turkey) ' FILE:\\\c:\users\user001\documents\FinancialInfo.txt ' Access allowed: TrueРазличия в сопоставлениях регистров между инвариантной культурой и всеми другими культурами. В таких случаях использование правил инвариантной культуры для преобразования символов в прописные или строчные буквы возвращает тот же символ. Для всех остальных культур возвращается другой символ. Некоторые затронутые символы перечислены в следующей таблице.
Символ Если изменено на Возвраты знак микрон (U+00B5) Верхний регистр ЗАГЛАВНАЯ ГРЕЧЕСКАЯ БУКВА МЮ (U+039C) ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА I С ТОЧКОЙ СВЕРХУ (U+0130) Нижний регистр ЛАТИНСКАЯ МАЛЕНЬКАЯ БУКВА I (U+0069) ЛАТИНСКАЯ МАЛЕНЬКАЯ БУКВА БЕЗ ТОЧКИ И (U+0131) Верхний регистр ЛАТИНСКАЯ БУКВА I (U+0049) ЛАТИНСКАЯ МАЛЕНЬКАЯ БУКВА LONG S (U+017F) Верхний регистр ЛАТИНСКАЯ ПРОПИСНАЯ БУКВА S (U+0053) ЛАТИНСКАЯ БУКВА D С НЕБОЛЬШОЙ БУКВОЙ Z С КАРОН (U+01C5) Нижний регистр ЛАТИНСКАЯ МАЛЕНЬКАЯ БУКВА DZ С CARON (U+01C6) ОБЪЕДИНЕНИЕ ГРЕЧЕСКИХ YPOGEGRAMMENI (U+0345) Верхний регистр ГРЕЧЕСКАЯ ЗАГЛАВНАЯ БУКВА ИОТА (U+0399) Различия в сопоставлениях двухбуквленных пар смешанного регистра в диапазоне символов ASCII. В большинстве культур двухбуквенная пара со смешанным регистром равна эквивалентной двухбуквенной паре в верхнем или нижнем регистре. Это не верно для следующих двухбуквенных пар в следующих языках и их региональных вариантах, так как в каждом таком случае они сравниваются с диграфом.
- "lJ" и "nJ" в культуре hr-HR (Хорватская (Хорватия)).
- "cH" в культурах cs-CZ (чешская (Чехия)) и sk-SK (словацкая (Словакия)).
- "aA" в культуре da-DK (Датская (Дания)).
- "cS", "dZ", "dZS", "nY", "sZ", "tY" и "zS" в культуре hu-HU (Венгерская (Венгрия)).
- "cH" и "lL" в культуре es-ES_tradnl (испанский (Испания, традиционная сортировка)).
- "cH", "gI", "kH", "nG", "nH", "pH", "qU'", "tH" и "tR" в культуре vi-VN (Вьетнам).
Тем не менее, редко встречается ситуация, в которой сравнение этих пар с учетом особенностей языка и культуры создает проблемы, поскольку такие пары редко встречаются в фиксированных строках или идентификаторах.
В следующем примере иллюстрируются некоторые различия в правилах изменения регистра между культурами при преобразовании строк в верхний регистр.
using System;
using System.Globalization;
using System.IO;
public class Example
{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\case.txt");
string[] words = { "file", "sıfır", "Dženana" };
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
new CultureInfo("en-US"),
new CultureInfo("tr-TR") };
foreach (var word in words) {
sw.WriteLine("{0}:", word);
foreach (var culture in cultures) {
string name = String.IsNullOrEmpty(culture.Name) ?
"Invariant" : culture.Name;
string upperWord = word.ToUpper(culture);
sw.WriteLine(" {0,10}: {1,7} {2, 38}", name,
upperWord, ShowHexValue(upperWord));
}
sw.WriteLine();
}
sw.Close();
}
private static string ShowHexValue(string s)
{
string retval = null;
foreach (var ch in s) {
byte[] bytes = BitConverter.GetBytes(ch);
retval += String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0]);
}
return retval;
}
}
// The example displays the following output:
// file:
// Invariant: FILE 00 46 00 49 00 4C 00 45
// en-US: FILE 00 46 00 49 00 4C 00 45
// tr-TR: FİLE 00 46 01 30 00 4C 00 45
//
// sıfır:
// Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52
// en-US: SIFIR 00 53 00 49 00 46 00 49 00 52
// tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52
//
// Dženana:
// Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41
// en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
// tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
open System
open System.Globalization
open System.IO
let showHexValue (s: string) =
let mutable retval = ""
for ch in s do
let bytes = BitConverter.GetBytes ch
retval <- retval + String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0])
retval
do
use sw = new StreamWriter(@".\case.txt")
let words = [| "file"; "sıfır"; "Dženana" |]
let cultures =
[| CultureInfo.InvariantCulture
CultureInfo "en-US"
CultureInfo "tr-TR" |]
for word in words do
sw.WriteLine("{0}:", word)
for culture in cultures do
let name =
if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name
let upperWord = word.ToUpper culture
sw.WriteLine(" {0,10}: {1,7} {2, 38}", name, upperWord, showHexValue upperWord)
sw.WriteLine()
sw.Close()
// The example displays the following output:
// file:
// Invariant: FILE 00 46 00 49 00 4C 00 45
// en-US: FILE 00 46 00 49 00 4C 00 45
// tr-TR: FİLE 00 46 01 30 00 4C 00 45
//
// sıfır:
// Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52
// en-US: SIFIR 00 53 00 49 00 46 00 49 00 52
// tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52
//
// Dženana:
// Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41
// en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
// tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
Imports System.Globalization
Imports System.IO
Module Example1
Public Sub Main()
Dim sw As New StreamWriter(".\case.txt")
Dim words As String() = {"file", "sıfır", "Dženana"}
Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
New CultureInfo("en-US"),
New CultureInfo("tr-TR")}
For Each word In words
sw.WriteLine("{0}:", word)
For Each culture In cultures
Dim name As String = If(String.IsNullOrEmpty(culture.Name),
"Invariant", culture.Name)
Dim upperWord As String = word.ToUpper(culture)
sw.WriteLine(" {0,10}: {1,7} {2, 38}", name,
upperWord, ShowHexValue(upperWord))
Next
sw.WriteLine()
Next
sw.Close()
End Sub
Private Function ShowHexValue(s As String) As String
Dim retval As String = Nothing
For Each ch In s
Dim bytes() As Byte = BitConverter.GetBytes(ch)
retval += String.Format("{0:X2} {1:X2} ", bytes(1), bytes(0))
Next
Return retval
End Function
End Module
' The example displays the following output:
' file:
' Invariant: FILE 00 46 00 49 00 4C 00 45
' en-US: FILE 00 46 00 49 00 4C 00 45
' tr-TR: FİLE 00 46 01 30 00 4C 00 45
'
' sıfır:
' Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52
' en-US: SIFIR 00 53 00 49 00 46 00 49 00 52
' tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52
'
' Dženana:
' Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41
' en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
' tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
Анализ и форматирование
Форматирование и синтаксический анализ являются обратными операциями. Правила форматирования определяют, как преобразовать значение, например дату и время или число, в его строковое представление, в то время как правила синтаксического анализа определяют, как преобразовать строковое представление в значение, например дату и время. Правила форматирования и анализа зависят от культурных соглашений. В следующем примере демонстрируется неоднозначность, которая может возникнуть при интерпретации строки даты, зависящей от культурных особенностей. Без знания культурных соглашений, использовавшихся при создании строки с датой, невозможно определить, означают ли 03.01.2011, 3.1.2011 и 01.03.2011 3 января 2011 года или 1 марта 2011 года.
using System;
using System.Globalization;
public class Example9
{
public static void Main()
{
DateTime date = new DateTime(2011, 3, 1);
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
new CultureInfo("en-US"),
new CultureInfo("fr-FR") };
foreach (var culture in cultures)
Console.WriteLine("{0,-12} {1}", String.IsNullOrEmpty(culture.Name) ?
"Invariant" : culture.Name,
date.ToString("d", culture));
}
}
// The example displays the following output:
// Invariant 03/01/2011
// en-US 3/1/2011
// fr-FR 01/03/2011
open System
open System.Globalization
let date = DateTime(2011, 3, 1)
let cultures =
[| CultureInfo.InvariantCulture
CultureInfo "en-US"
CultureInfo "fr-FR" |]
for culture in cultures do
printfn $"""{(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),-12} {date.ToString("d", culture)}"""
// The example displays the following output:
// Invariant 03/01/2011
// en-US 3/1/2011
// fr-FR 01/03/2011
Imports System.Globalization
Module Example8
Public Sub Main()
Dim dat As Date = #3/1/2011#
Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
New CultureInfo("en-US"),
New CultureInfo("fr-FR")}
For Each culture In cultures
Console.WriteLine("{0,-12} {1}", If(String.IsNullOrEmpty(culture.Name),
"Invariant", culture.Name),
dat.ToString("d", culture))
Next
End Sub
End Module
' The example displays the following output:
' Invariant 03/01/2011
' en-US 3/1/2011
' fr-FR 01/03/2011
Аналогично, как видно из следующего примера, одна строка может давать разные даты в зависимости от культуры, чьи соглашения используются при разборе.
using System;
using System.Globalization;
public class Example15
{
public static void Main()
{
string dateString = "07/10/2011";
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
CultureInfo.CreateSpecificCulture("en-GB"),
CultureInfo.CreateSpecificCulture("en-US") };
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}\n", "Date String", "Culture",
"Month", "Day");
foreach (var culture in cultures) {
DateTime date = DateTime.Parse(dateString, culture);
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString,
String.IsNullOrEmpty(culture.Name) ?
"Invariant" : culture.Name,
date.Month, date.Day);
}
}
}
// The example displays the following output:
// Date String Culture Month Day
//
// 07/10/2011 Invariant 7 10
// 07/10/2011 en-GB 10 7
// 07/10/2011 en-US 7 10
open System
open System.Globalization
let dateString = "07/10/2011"
let cultures =
[| CultureInfo.InvariantCulture
CultureInfo.CreateSpecificCulture "en-GB"
CultureInfo.CreateSpecificCulture "en-US" |]
printfn $"""{"Date String",-12} {"Culture",10} {"Month",8} {"Day",8}\n"""
for culture in cultures do
let date = DateTime.Parse(dateString, culture)
printfn $"""{dateString,-12} {(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),10} {date.Month,8} {date.Day,8}"""
// The example displays the following output:
// Date String Culture Month Day
//
// 07/10/2011 Invariant 7 10
// 07/10/2011 en-GB 10 7
// 07/10/2011 en-US 7 10
Imports System.Globalization
Module Example18
Public Sub Main()
Dim dateString As String = "07/10/2011"
Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
CultureInfo.CreateSpecificCulture("en-GB"),
CultureInfo.CreateSpecificCulture("en-US")}
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", "Date String", "Culture",
"Month", "Day")
Console.WriteLine()
For Each culture In cultures
Dim dat As Date = DateTime.Parse(dateString, culture)
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString,
If(String.IsNullOrEmpty(culture.Name),
"Invariant", culture.Name),
dat.Month, dat.Day)
Next
End Sub
End Module
' The example displays the following output:
' Date String Culture Month Day
'
' 07/10/2011 Invariant 7 10
' 07/10/2011 en-GB 10 7
' 07/10/2011 en-US 7 10
Сравнение строк и сортировка
Практики сравнения и сортировки строк различаются в зависимости от культурных особенностей. Например, порядок сортировки может зависеть от фонетики или визуального представления символов. На восточноазиатских языках символы сортируются по росчерку и радикалу иеографов. Сортировка также зависит от порядка алфавитов, используемого в разных языках и культурах. Например, датский язык имеет символ "Æ", который сортируется после "Z" в алфавите. Кроме того, сравнения могут быть чувствительными или нечувствительными к регистру, а правила регистра могут различаться в зависимости от языка и культуры. С другой стороны, порядковое сравнение использует кодовые точки Юникода отдельных символов в строке при сравнении и сортировке строк.
Правила сортировки определяют алфавитный порядок символов Юникода и как две строки сравниваются друг с другом. Например, метод String.Compare(String, String, StringComparison) сравнивает две строки согласно параметру StringComparison. Если значение параметра равно StringComparison.CurrentCulture, метод выполняет лингвистическое сравнение, которое использует соглашения текущей культуры; если значение параметра равно StringComparison.Ordinal, метод выполняет порядковое сравнение. Следовательно, как показано в следующем примере, если текущий язык и региональные параметры — английский США, первый вызов метода (с учетом языка и региональных параметров) считает "a" меньше "A", но второй вызов того же метода (с использованием порядкового сравнения) считает "a" больше "A".
using System;
using System.Globalization;
using System.Threading;
public class Example2
{
public static void Main()
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture));
Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal));
}
}
// The example displays the following output:
// 1
// -32
open System
open System.Globalization
open System.Threading
Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "en-US"
printfn $"""{String.Compare("A", "a", StringComparison.CurrentCulture)}"""
printfn $"""{String.Compare("A", "a", StringComparison.Ordinal)}"""
// The example displays the following output:
// 1
// -32
Imports System.Globalization
Imports System.Threading
Module Example3
Public Sub Main()
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture))
Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal))
End Sub
End Module
' The example displays the following output:
' 1
' -32
.NET поддерживает правила сортировки слов, строк и порядковых номеров:
Сортировка слов выполняет культурно-зависимое сравнение строк с учетом культурных особенностей, в которых некоторые неалфавитно-цифровые символы Юникода могут иметь специальные веса, назначенные им. Например, дефис (-) может иметь очень небольшой вес, который ему назначили, чтобы "coop" и "co-op" отображались рядом друг с другом в отсортированном списке. Список String методов, которые сравнивают две строки с помощью правил сортировки слов, см. раздел "Операции со строками по категориям".
Сортировка строк также выполняет сравнение с учетом языка и региональных параметров. Это похоже на сортировку слов, за исключением того, что нет особых случаев, и все неалфавитно-цифровые символы расположены перед всеми буквенно-цифровыми символами Юникода. Две строки можно сравнить с помощью правил сортировки строк, посредством вызова перегруженных методов, которые принимают параметр
optionsс заданным значением CompareOptions.StringSort. Обратите внимание, что это единственный метод, который предоставляет .NET для сравнения двух строк с помощью правил сортировки строк.Порядковый сорт сравнивает строки на основе числового значения каждого Char объекта в строке. Порядковое сравнение автоматически учитывает регистр, так как строчные и верхние версии символа имеют разные кодовые точки. Однако если регистр не важен, можно указать порядковое сравнение, которое игнорирует регистр. Это эквивалентно преобразованию строки в верхний регистр с использованием инвариантной культуры, а затем выполнению порядкового сравнения. Список String методов, которые сравнивают две строки с использованием правил порядковой сортировки, см. в разделе "Строки" по категориям .
Чувствительное к культуре сравнение — это любое сравнение, которое явно или неявно использует объект CultureInfo, включая инвариантные настройки культуры, указанные свойством CultureInfo.InvariantCulture. Неявная культура — это текущая культура, которая задается свойствами Thread.CurrentCulture и CultureInfo.CurrentCulture. Существует существенная вариативность в порядке сортировки алфавитных символов (т. е. символов, для которых Char.IsLetter свойство возвращается true) в разных культурах. Можно указать сравнение, учитывающее культурные особенности, использовав соглашения определенной культуры, передав объект CultureInfo в метод сравнения строк, например Compare(String, String, CultureInfo, CompareOptions). Можно указать сравнение с учетом культурных особенностей, которое использует соглашения текущей культуры, указав StringComparison.CurrentCulture, StringComparison.CurrentCultureIgnoreCase или любой элемент перечисления CompareOptions, кроме CompareOptions.Ordinal или CompareOptions.OrdinalIgnoreCase, для соответствующей перегрузки метода Compare. Сравнение с учетом языка и региональных параметров обычно подходит для сортировки, в то время как порядковое сравнение для этого не подходит. Порядковое сравнение обычно подходит для определения того, равны ли две строки (т. е. для определения идентичности), тогда как сравнение с учетом языковых и региональных параметров не является подходящим.
В следующем примере показано различие между культурно-зависимым и порядковым сравнением. В примере оцениваются три строки: Apple, Æble и AEble, путем использования порядкового сравнения и языковых и региональных параметров da-DK и en-US (каждый из которых является языковыми и региональными настройками по умолчанию на момент вызова метода Compare). Поскольку датский язык рассматривает символ "Æ" как отдельную букву и сортирует его после "Z" в алфавите, строка "Æble" больше "Apple". Тем не менее, "Æble" не считается эквивалентным "AEble", поэтому "Æble" больше, чем "AEble". Культура en-US не включает букву "Æ", но рассматривает его как эквивалент "AE", что объясняет, почему "Æble" меньше "Apple", но равно "AEble". Порядковое сравнение, с другой стороны, считает "Apple" меньше, чем "Æble", и "Æble" больше, чем "AEble".
using System;
using System.Globalization;
using System.Threading;
public class CompareStringSample
{
public static void Main()
{
string str1 = "Apple";
string str2 = "Æble";
string str3 = "AEble";
// Set the current culture to Danish in Denmark.
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
Console.WriteLine($"Current culture: {CultureInfo.CurrentCulture.Name}");
Console.WriteLine($"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}");
Console.WriteLine($"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n");
// Set the current culture to English in the U.S.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine($"Current culture: {CultureInfo.CurrentCulture.Name}");
Console.WriteLine($"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}");
Console.WriteLine($"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n");
// Perform an ordinal comparison.
Console.WriteLine("Ordinal comparison");
Console.WriteLine($"Comparison of {str1} with {str2}: {String.Compare(str1, str2, StringComparison.Ordinal)}");
Console.WriteLine($"Comparison of {str2} with {str3}: {String.Compare(str2, str3, StringComparison.Ordinal)}");
}
}
// The example displays the following output:
// Current culture: da-DK
// Comparison of Apple with Æble: -1
// Comparison of Æble with AEble: 1
//
// Current culture: en-US
// Comparison of Apple with Æble: 1
// Comparison of Æble with AEble: 0
//
// Ordinal comparison
// Comparison of Apple with Æble: -133
// Comparison of Æble with AEble: 133
open System
open System.Globalization
open System.Threading
let str1 = "Apple"
let str2 = "Æble"
let str3 = "AEble"
// Set the current culture to Danish in Denmark.
Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK"
printfn $"Current culture: {CultureInfo.CurrentCulture.Name}"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n"
// Set the current culture to English in the U.S.
Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US"
printfn $"Current culture: {CultureInfo.CurrentCulture.Name}"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n"
// Perform an ordinal comparison.
printfn "Ordinal comparison"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2, StringComparison.Ordinal)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3, StringComparison.Ordinal)}"
// The example displays the following output:
// Current culture: da-DK
// Comparison of Apple with Æble: -1
// Comparison of Æble with AEble: 1
//
// Current culture: en-US
// Comparison of Apple with Æble: 1
// Comparison of Æble with AEble: 0
//
// Ordinal comparison
// Comparison of Apple with Æble: -133
// Comparison of Æble with AEble: 133
Imports System.Globalization
Imports System.Threading
Public Module Example6
Public Sub Main()
Dim str1 As String = "Apple"
Dim str2 As String = "Æble"
Dim str3 As String = "AEble"
' Set the current culture to Danish in Denmark.
Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK")
Console.WriteLine("Current culture: {0}",
CultureInfo.CurrentCulture.Name)
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2, String.Compare(str1, str2))
Console.WriteLine("Comparison of {0} with {1}: {2}",
str2, str3, String.Compare(str2, str3))
Console.WriteLine()
' Set the current culture to English in the U.S.
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Console.WriteLine("Current culture: {0}",
CultureInfo.CurrentCulture.Name)
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2, String.Compare(str1, str2))
Console.WriteLine("Comparison of {0} with {1}: {2}",
str2, str3, String.Compare(str2, str3))
Console.WriteLine()
' Perform an ordinal comparison.
Console.WriteLine("Ordinal comparison")
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2,
String.Compare(str1, str2, StringComparison.Ordinal))
Console.WriteLine("Comparison of {0} with {1}: {2}",
str2, str3,
String.Compare(str2, str3, StringComparison.Ordinal))
End Sub
End Module
' The example displays the following output:
' Current culture: da-DK
' Comparison of Apple with Æble: -1
' Comparison of Æble with AEble: 1
'
' Current culture: en-US
' Comparison of Apple with Æble: 1
' Comparison of Æble with AEble: 0
'
' Ordinal comparison
' Comparison of Apple with Æble: -133
' Comparison of Æble with AEble: 133
Используйте следующие общие рекомендации, чтобы выбрать подходящий метод сортировки или сравнения строк:
Если вы хотите, чтобы строки были упорядочены в соответствии с культурой пользователя, их следует упорядочить на основе соглашений текущей культуры. Если культура пользователя изменяется, порядок отсортированных строк также изменится соответствующим образом. Например, приложение тезауруса всегда должно сортировать слова на основе культуры пользователя.
Если вы хотите, чтобы строки были упорядочены в соответствии с соглашениями определенной культуры, упорядочьте их, передав объект CultureInfo, представляющий эту культуру, в метод сравнения. Например, в приложении, предназначенном для обучения учащихся определенному языку, строки должны быть упорядочены в соответствии с традициями одной из культур, говорящих на этом языке.
Если вы хотите, чтобы порядок строк оставался неизменным в разных культурах, их следует упорядочить на основе соглашений инвариантной культуры или использовать порядковое сравнение. Например, для того чтобы упорядочить имена файлов, процессов, мьютексов или именованных каналов, используется порядковая сортировка.
Для сравнения, включающего решение безопасности (например, допустимо ли имя пользователя), всегда следует выполнить порядковый тест на равенство путем вызова перегрузки Equals метода.
Примечание.
Правила сортировки и обработки с учетом культурных особенностей, используемые при сравнении строк, зависят от версии платформы .NET. В .NET Core сравнение строк зависит от версии Стандарта Юникода, поддерживаемой основной операционной системой. В платформе .NET Framework версии 4.5 и более поздних версий, работающих на Windows 8 или более поздних версиях, сортировка, изменение регистра, нормализация и информация о символах Unicode соответствуют стандарту Unicode 6.0. В других операционных системах Windows они соответствуют стандарту Юникода 5.0.
Дополнительные сведения о правилах сортировки слов, строк и порядковой сортировки см. в System.Globalization.CompareOptions разделе. См. Лучшие практики использования строк для получения дополнительных рекомендаций по использованию каждого правила.
Обычно методы Compare сравнения строк не вызываются напрямую, чтобы определить порядок сортировки строк. Вместо этого методы сравнения вызываются методами сортировки, такими как Array.Sort или List<T>.Sort. В следующем примере выполняются четыре различных операции сортировки: сортировка слов с использованием текущих языковых и региональных параметров, сортировка слов с использованием инвариантных языковых и региональных параметров, сортировка по порядку и сортировка строк с использованием инвариантных языковых и региональных параметров. Это делается без явного вызова метода сравнения строк, хотя они указывают тип сравнения, который следует использовать. Обратите внимание, что каждый тип сортировки создает уникальное упорядочение строк в своем массиве.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
public class Example3
{
public static void Main()
{
string[] strings = { "coop", "co-op", "cooperative",
"co\u00ADoperative", "cœur", "coeur" };
// Perform a word sort using the current (en-US) culture.
string[] current = new string[strings.Length];
strings.CopyTo(current, 0);
Array.Sort(current, StringComparer.CurrentCulture);
// Perform a word sort using the invariant culture.
string[] invariant = new string[strings.Length];
strings.CopyTo(invariant, 0);
Array.Sort(invariant, StringComparer.InvariantCulture);
// Perform an ordinal sort.
string[] ordinal = new string[strings.Length];
strings.CopyTo(ordinal, 0);
Array.Sort(ordinal, StringComparer.Ordinal);
// Perform a string sort using the current culture.
string[] stringSort = new string[strings.Length];
strings.CopyTo(stringSort, 0);
Array.Sort(stringSort, new SCompare());
// Display array values
Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}\n",
"Original", "Word Sort", "Invariant Word",
"Ordinal Sort", "String Sort");
for (int ctr = 0; ctr < strings.Length; ctr++)
Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
strings[ctr], current[ctr], invariant[ctr],
ordinal[ctr], stringSort[ctr] );
}
}
// IComparer<String> implementation to perform string sort.
internal class SCompare : IComparer<String>
{
public int Compare(string x, string y)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort);
}
}
// The example displays the following output:
// Original Word Sort Invariant Word Ordinal Sort String Sort
//
// coop cœur cœur co-op co-op
// co-op coeur coeur coeur cœur
// cooperative coop coop coop coeur
// cooperative co-op co-op cooperative coop
// cœur cooperative cooperative cooperative cooperative
// coeur cooperative cooperative cœur cooperative
open System
open System.Collections.Generic
open System.Globalization
// IComparer<String> implementation to perform string sort using an F# object expression.
let scompare =
{ new IComparer<String> with
member _.Compare(x, y) =
CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort) }
let strings = [| "coop"; "co-op"; "cooperative"; "co\u00ADoperative"; "cœur"; "coeur" |]
// Perform a word sort using the current (en-US) culture.
let current = Array.copy strings
Array.Sort(current, StringComparer.CurrentCulture)
// Perform a word sort using the invariant culture.
let invariant = Array.copy strings
Array.Sort(invariant, StringComparer.InvariantCulture)
// Perform an ordinal sort.
let ordinal = Array.copy strings
Array.Sort(ordinal, StringComparer.Ordinal)
// Perform a string sort using the current culture.
let stringSort = Array.copy strings
Array.Sort(stringSort, scompare)
// Display array values
printfn "%13s %13s %15s %13s %13s\n" "Original" "Word Sort" "Invariant Word" "Ordinal Sort" "String Sort"
for i = 0 to strings.Length - 1 do
printfn "%13s %13s %15s %13s %13s\n" strings[i] current[i] invariant[i] ordinal[i] stringSort[i]
// The example displays the following output:
// Original Word Sort Invariant Word Ordinal Sort String Sort
//
// coop cœur cœur co-op co-op
// co-op coeur coeur coeur cœur
// cooperative coop coop coop coeur
// cooperative co-op co-op cooperative coop
// cœur cooperative cooperative cooperative cooperative
// coeur cooperative cooperative cœur cooperative
Imports System.Collections
Imports System.Collections.Generic
Imports System.Globalization
Module Example4
Public Sub Main()
Dim strings() As String = {"coop", "co-op", "cooperative",
"co" + ChrW(&HAD) + "operative",
"cœur", "coeur"}
' Perform a word sort using the current (en-US) culture.
Dim current(strings.Length - 1) As String
strings.CopyTo(current, 0)
Array.Sort(current, StringComparer.CurrentCulture)
' Perform a word sort using the invariant culture.
Dim invariant(strings.Length - 1) As String
strings.CopyTo(invariant, 0)
Array.Sort(invariant, StringComparer.InvariantCulture)
' Perform an ordinal sort.
Dim ordinal(strings.Length - 1) As String
strings.CopyTo(ordinal, 0)
Array.Sort(ordinal, StringComparer.Ordinal)
' Perform a string sort using the current culture.
Dim stringSort(strings.Length - 1) As String
strings.CopyTo(stringSort, 0)
Array.Sort(stringSort, New SCompare())
' Display array values
Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
"Original", "Word Sort", "Invariant Word",
"Ordinal Sort", "String Sort")
Console.WriteLine()
For ctr As Integer = 0 To strings.Length - 1
Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
strings(ctr), current(ctr), invariant(ctr),
ordinal(ctr), stringSort(ctr))
Next
End Sub
End Module
' IComparer<String> implementation to perform string sort.
Friend Class SCompare : Implements IComparer(Of String)
Public Function Compare(x As String, y As String) As Integer _
Implements IComparer(Of String).Compare
Return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort)
End Function
End Class
' The example displays the following output:
' Original Word Sort Invariant Word Ordinal Sort String Sort
'
' coop cœur cœur co-op co-op
' co-op coeur coeur coeur cœur
' cooperative coop coop coop coeur
' cooperative co-op co-op cooperative coop
' cœur cooperative cooperative cooperative cooperative
' coeur cooperative cooperative cœur cooperative
Совет
Внутри .NET использует ключи сортировки для поддержки сравнения строк с учетом культуры. Каждый символ в строке имеет несколько категорий весов сортировки, включая алфавитные, регистр и диакритические знаки. Ключ сортировки, представленный SortKey классом, предоставляет репозиторий этих весов для определенной строки. Если приложение выполняет большое количество операций поиска или сортировки в одном наборе строк, можно повысить производительность, создав и сохраняя ключи сортировки для всех строк, которые он использует. Если требуется операция сортировки или сравнения, вместо строк используйте ключи сортировки. Дополнительные сведения см. в описании класса SortKey.
Если вы не указываете соглашение о сравнении строк, методы сортировки, такие как Array.Sort(Array), выполняют чувствительную к языку и регистру сортировку строк. В следующем примере показано, как изменение текущей культуры влияет на порядок отсортированных строк в массиве. Он создает массив из трех строк. Сначала устанавливается свойство System.Threading.Thread.CurrentThread.CurrentCulture на английский (США) и вызывается метод Array.Sort(Array). Результирующий порядок сортировки основан на соглашениях о сортировке для культуры английского языка (США). Далее в примере свойство System.Threading.Thread.CurrentThread.CurrentCulture устанавливается в значение da-DK, и метод Array.Sort вызывается снова. Обратите внимание, что результирующий порядок сортировки отличается от результатов en-US, так как он использует соглашения сортировки для Датской (Дании).
using System;
using System.Globalization;
using System.Threading;
public class ArraySort
{
public static void Main(String[] args)
{
// Create and initialize a new array to store the strings.
string[] stringArray = { "Apple", "Æble", "Zebra"};
// Display the values of the array.
Console.WriteLine( "The original string array:");
PrintIndexAndValues(stringArray);
// Set the CurrentCulture to "en-US".
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
// Sort the values of the array.
Array.Sort(stringArray);
// Display the values of the array.
Console.WriteLine("After sorting for the culture \"en-US\":");
PrintIndexAndValues(stringArray);
// Set the CurrentCulture to "da-DK".
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
// Sort the values of the Array.
Array.Sort(stringArray);
// Display the values of the array.
Console.WriteLine("After sorting for the culture \"da-DK\":");
PrintIndexAndValues(stringArray);
}
public static void PrintIndexAndValues(string[] myArray)
{
for (int i = myArray.GetLowerBound(0); i <=
myArray.GetUpperBound(0); i++ )
Console.WriteLine($"[{i}]: {myArray[i]}");
Console.WriteLine();
}
}
// The example displays the following output:
// The original string array:
// [0]: Apple
// [1]: Æble
// [2]: Zebra
//
// After sorting for the "en-US" culture:
// [0]: Æble
// [1]: Apple
// [2]: Zebra
//
// After sorting for the culture "da-DK":
// [0]: Apple
// [1]: Zebra
// [2]: Æble
open System
open System.Globalization
open System.Threading
let printIndexAndValues (myArray: string[]) =
for i = myArray.GetLowerBound 0 to myArray.GetUpperBound 0 do
printfn $"[{i}]: {myArray[i]}"
printfn ""
// Create and initialize a new array to store the strings.
let stringArray = [| "Apple"; "Æble"; "Zebra" |]
// Display the values of the array.
printfn "The original string array:"
printIndexAndValues stringArray
// Set the CurrentCulture to "en-US".
Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US"
// Sort the values of the array.
Array.Sort stringArray
// Display the values of the array.
printfn "After sorting for the culture \"en-US\":"
printIndexAndValues stringArray
// Set the CurrentCulture to "da-DK".
Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK"
// Sort the values of the Array.
Array.Sort stringArray
// Display the values of the array.
printfn "After sorting for the culture \"da-DK\":"
printIndexAndValues stringArray
// The example displays the following output:
// The original string array:
// [0]: Apple
// [1]: Æble
// [2]: Zebra
//
// After sorting for the "en-US" culture:
// [0]: Æble
// [1]: Apple
// [2]: Zebra
//
// After sorting for the culture "da-DK":
// [0]: Apple
// [1]: Zebra
// [2]: Æble
Imports System.Globalization
Imports System.IO
Imports System.Threading
Public Class TextToFile
Public Shared Sub Main()
' Creates and initializes a new array to store
' these date/time objects.
Dim stringArray() As String = { "Apple", "Æble", "Zebra"}
' Displays the values of the array.
Console.WriteLine("The original string array:")
PrintIndexAndValues(stringArray)
' Set the CurrentCulture to "en-US".
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
' Sort the values of the Array.
Array.Sort(stringArray)
' Display the values of the array.
Console.WriteLine("After sorting for the ""en-US"" culture:")
PrintIndexAndValues(stringArray)
' Set the CurrentCulture to "da-DK".
Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK")
' Sort the values of the Array.
Array.Sort(stringArray)
' Displays the values of the Array.
Console.WriteLine("After sorting for the culture ""da-DK"":")
PrintIndexAndValues(stringArray)
End Sub
Public Shared Sub PrintIndexAndValues(myArray() As String)
For i As Integer = myArray.GetLowerBound(0) To myArray.GetUpperBound(0)
Console.WriteLine("[{0}]: {1}", i, myArray(i))
Next
Console.WriteLine()
End Sub
End Class
' The example displays the following output:
' The original string array:
' [0]: Apple
' [1]: Æble
' [2]: Zebra
'
' After sorting for the "en-US" culture:
' [0]: Æble
' [1]: Apple
' [2]: Zebra
'
' After sorting for the culture "da-DK":
' [0]: Apple
' [1]: Zebra
' [2]: Æble
Предупреждение
Если основная цель в сравнении строк заключается в определении того, равны ли они, следует вызвать String.Equals метод. Как правило, для выполнения порядкового сравнения следует использовать Equals . Метод String.Compare предназначен в первую очередь для сортировки строк.
Методы поиска строк, такие как String.StartsWith и String.IndexOf, также могут выполнять сравнения строк с учетом культурных особенностей или их порядков. В следующем примере показаны различия между порядковыми сравнениями и культурно-значимыми сравнениями, используемыми с методом IndexOf. Поиск с учетом культурных особенностей, в котором текущая культура — английский (США), рассматривает подстроку "oe" как совпадающую с лигатурой "œ". Так как мягкий дефис (U+00AD) является символом нулевой ширины, поиск обрабатывает мягкий дефис как эквивалентный String.Empty и находит совпадение в начале строки. Порядковый поиск, с другой стороны, не находит совпадения в любом случае.
using System;
public class Example8
{
public static void Main()
{
// Search for "oe" and "œu" in "œufs" and "oeufs".
string s1 = "œufs";
string s2 = "oeufs";
FindInString(s1, "oe", StringComparison.CurrentCulture);
FindInString(s1, "oe", StringComparison.Ordinal);
FindInString(s2, "œu", StringComparison.CurrentCulture);
FindInString(s2, "œu", StringComparison.Ordinal);
Console.WriteLine();
string s3 = "co\u00ADoperative";
FindInString(s3, "\u00AD", StringComparison.CurrentCulture);
FindInString(s3, "\u00AD", StringComparison.Ordinal);
}
private static void FindInString(string s, string substring, StringComparison options)
{
int result = s.IndexOf(substring, options);
if (result != -1)
Console.WriteLine($"'{substring}' found in {s} at position {result}");
else
Console.WriteLine($"'{substring}' not found in {s}");
}
}
// The example displays the following output:
// 'oe' found in œufs at position 0
// 'oe' not found in œufs
// 'œu' found in oeufs at position 0
// 'œu' not found in oeufs
//
// '' found in cooperative at position 0
// '' found in cooperative at position 2
open System
let findInString (s: string) (substring: string) (options: StringComparison) =
let result = s.IndexOf(substring, options)
if result <> -1 then
printfn $"'{substring}' found in {s} at position {result}"
else
printfn $"'{substring}' not found in {s}"
// Search for "oe" and "œu" in "œufs" and "oeufs".
let s1 = "œufs"
let s2 = "oeufs"
findInString s1 "oe" StringComparison.CurrentCulture
findInString s1 "oe" StringComparison.Ordinal
findInString s2 "œu" StringComparison.CurrentCulture
findInString s2 "œu" StringComparison.Ordinal
printfn ""
let s3 = "co\u00ADoperative"
findInString s3 "\u00AD" StringComparison.CurrentCulture
findInString s3 "\u00AD" StringComparison.Ordinal
// The example displays the following output:
// 'oe' found in œufs at position 0
// 'oe' not found in œufs
// 'œu' found in oeufs at position 0
// 'œu' not found in oeufs
//
// '' found in cooperative at position 0
// '' found in cooperative at position 2
Module Example5
Public Sub Main()
' Search for "oe" and "œu" in "œufs" and "oeufs".
Dim s1 As String = "œufs"
Dim s2 As String = "oeufs"
FindInString(s1, "oe", StringComparison.CurrentCulture)
FindInString(s1, "oe", StringComparison.Ordinal)
FindInString(s2, "œu", StringComparison.CurrentCulture)
FindInString(s2, "œu", StringComparison.Ordinal)
Console.WriteLine()
Dim softHyphen As String = ChrW(&HAD)
Dim s3 As String = "co" + softHyphen + "operative"
FindInString(s3, softHyphen, StringComparison.CurrentCulture)
FindInString(s3, softHyphen, StringComparison.Ordinal)
End Sub
Private Sub FindInString(s As String, substring As String,
options As StringComparison)
Dim result As Integer = s.IndexOf(substring, options)
If result <> -1 Then
Console.WriteLine("'{0}' found in {1} at position {2}",
substring, s, result)
Else
Console.WriteLine("'{0}' not found in {1}",
substring, s)
End If
End Sub
End Module
' The example displays the following output:
' 'oe' found in œufs at position 0
' 'oe' not found in œufs
' 'œu' found in oeufs at position 0
' 'œu' not found in oeufs
'
' '' found in cooperative at position 0
' '' found in cooperative at position 2
Поиск в строках
Методы поиска строк, такие как String.StartsWith и String.IndexOf, также могут выполнять сравнения строк с учетом языка и региональных параметров, чтобы определить, найден ли символ или подстрока в указанной строке.
Методы поиска в String классе, которые ищут отдельный символ, например IndexOf метод, или один из наборов символов, например IndexOfAny метод, все выполняют порядковый поиск. Чтобы выполнить поиск символа, учитывая язык и региональные настройки, необходимо вызвать такой метод, как CompareInfo.IndexOf(String, Char) или CompareInfo.LastIndexOf(String, Char). Обратите внимание, что результаты поиска символа с использованием порядкового и культурного сравнения могут значительно отличаться. Например, поиск предварительно составленного символа Юникода, например лигатуры "Æ" (U+00C6), может соответствовать любому вхождению его компонентов в правильной последовательности, например "AE" (U+041U+0045), в зависимости от культуры. В следующем примере показано различие между методом String.IndexOf(Char) и методом CompareInfo.IndexOf(String, Char) при поиске одного символа. Лигатура "æ" (U+00E6) присутствует в строке "aerial" при использовании культурных соглашений англоязычной (en-US) культуры, но отсутствует при использовании культурных соглашений датской (da-DK) культуры или при выполнении порядкового сравнения.
using System;
using System.Globalization;
public class Example17
{
public static void Main()
{
String[] cultureNames = { "da-DK", "en-US" };
CompareInfo ci;
String str = "aerial";
Char ch = 'æ'; // U+00E6
Console.Write("Ordinal comparison -- ");
Console.WriteLine($"Position of '{ch}' in {str}: {str.IndexOf(ch)}");
foreach (var cultureName in cultureNames) {
ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo;
Console.Write("{0} cultural comparison -- ", cultureName);
Console.WriteLine($"Position of '{ch}' in {str}: {ci.IndexOf(str, ch)}");
}
}
}
// The example displays the following output:
// Ordinal comparison -- Position of 'æ' in aerial: -1
// da-DK cultural comparison -- Position of 'æ' in aerial: -1
// en-US cultural comparison -- Position of 'æ' in aerial: 0
open System.Globalization
let cultureNames = [| "da-DK"; "en-US" |]
let str = "aerial"
let ch = 'æ' // U+00E6
printf "Ordinal comparison -- "
printfn $"Position of '{ch}' in {str}: {str.IndexOf ch}"
for cultureName in cultureNames do
let ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo
printf $"{cultureName} cultural comparison -- "
printfn $"Position of '{ch}' in {str}: {ci.IndexOf(str, ch)}"
// The example displays the following output:
// Ordinal comparison -- Position of 'æ' in aerial: -1
// da-DK cultural comparison -- Position of 'æ' in aerial: -1
// en-US cultural comparison -- Position of 'æ' in aerial: 0
Imports System.Globalization
Module Example19
Public Sub Main()
Dim cultureNames() As String = {"da-DK", "en-US"}
Dim ci As CompareInfo
Dim str As String = "aerial"
Dim ch As Char = "æ"c ' U+00E6
Console.Write("Ordinal comparison -- ")
Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
str.IndexOf(ch))
For Each cultureName In cultureNames
ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo
Console.Write("{0} cultural comparison -- ", cultureName)
Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
ci.IndexOf(str, ch))
Next
End Sub
End Module
' The example displays the following output:
' Ordinal comparison -- Position of 'æ' in aerial: -1
' da-DK cultural comparison -- Position of 'æ' in aerial: -1
' en-US cultural comparison -- Position of 'æ' in aerial: 0
С другой стороны, String методы класса, которые ищут строку, а не символ, выполняют поиск с учетом языка и региональных параметров, если параметры поиска явно не указаны параметром типа StringComparison. Единственным исключением является Contains, который выполняет порядковый поиск.
Проверка на равенство
String.Compare Используйте метод для определения связи двух строк в порядке сортировки. Как правило, это операция, чувствительная к культурным особенностям. Напротив, вызовите String.Equals метод для проверки на равенство. Так как тест на равенство обычно сравнивает входные данные пользователя с определенной известной строкой, например допустимым именем пользователя, паролем или путем файловой системы, обычно это порядковая операция.
Предупреждение
Можно проверить равенство путем вызова String.Compare метода и определения того, равно ли возвращаемое значение равно нулю. Однако эта практика не рекомендуется. Чтобы определить, равны ли две строки, следует вызвать одну из перегрузок метода String.Equals. Предпочтительная перегрузка для вызова — это метод экземпляра Equals(String, StringComparison) или статический Equals(String, String, StringComparison) метод, так как оба метода включают System.StringComparison параметр, который явно задает тип сравнения.
В следующем примере показана опасность выполнения сравнения с учётом культурных особенностей для проверки равенства, когда вместо этого следует использовать порядковое сравнение. В этом случае цель кода заключается в запрете доступа к файловой системе из URL-адресов, начинающихся с "FILE://" или "file://", путем сравнения без учета регистра начала URL-адреса со строкой "FILE://". Однако, если при сравнении с учетом языковых и региональных особенностей используется культура турецкого языка (Турция) для URL-адреса, который начинается с "file://", проверка на равенство не удается, так как турецкий верхний регистр для буквы "i" - это "İ", а не "I". В результате доступ к файловой системе непреднамеренно разрешен. С другой стороны, если выполняется порядковое сравнение, проверка на равенство проходит успешно, но доступ к файловой системе запрещен.
using System;
using System.Globalization;
using System.Threading;
public class Example4
{
public static void Main()
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR");
string filePath = "file://c:/notes.txt";
Console.WriteLine("Culture-sensitive test for equality:");
if (! TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase))
Console.WriteLine($"Access to {filePath} is allowed.");
else
Console.WriteLine($"Access to {filePath} is not allowed.");
Console.WriteLine("\nOrdinal test for equality:");
if (! TestForEquality(filePath, StringComparison.OrdinalIgnoreCase))
Console.WriteLine($"Access to {filePath} is allowed.");
else
Console.WriteLine($"Access to {filePath} is not allowed.");
}
private static bool TestForEquality(string str, StringComparison cmp)
{
int position = str.IndexOf("://");
if (position < 0) return false;
string substring = str.Substring(0, position);
return substring.Equals("FILE", cmp);
}
}
// The example displays the following output:
// Culture-sensitive test for equality:
// Access to file://c:/notes.txt is allowed.
//
// Ordinal test for equality:
// Access to file://c:/notes.txt is not allowed.
open System
open System.Globalization
open System.Threading
let testForEquality (str: string) (cmp: StringComparison) =
let position = str.IndexOf "://"
if position < 0 then false
else
let substring = str.Substring(0, position)
substring.Equals("FILE", cmp)
Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "tr-TR"
let filePath = "file://c:/notes.txt"
printfn "Culture-sensitive test for equality:"
if not (testForEquality filePath StringComparison.CurrentCultureIgnoreCase) then
printfn $"Access to {filePath} is allowed."
else
printfn $"Access to {filePath} is not allowed."
printfn "\nOrdinal test for equality:"
if not (testForEquality filePath StringComparison.OrdinalIgnoreCase) then
printfn $"Access to {filePath} is allowed."
else
printfn $"Access to {filePath} is not allowed."
// The example displays the following output:
// Culture-sensitive test for equality:
// Access to file://c:/notes.txt is allowed.
//
// Ordinal test for equality:
// Access to file://c:/notes.txt is not allowed.
Imports System.Globalization
Imports System.Threading
Module Example7
Public Sub Main()
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR")
Dim filePath As String = "file://c:/notes.txt"
Console.WriteLine("Culture-sensitive test for equality:")
If Not TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase) Then
Console.WriteLine("Access to {0} is allowed.", filePath)
Else
Console.WriteLine("Access to {0} is not allowed.", filePath)
End If
Console.WriteLine()
Console.WriteLine("Ordinal test for equality:")
If Not TestForEquality(filePath, StringComparison.OrdinalIgnoreCase) Then
Console.WriteLine("Access to {0} is allowed.", filePath)
Else
Console.WriteLine("Access to {0} is not allowed.", filePath)
End If
End Sub
Private Function TestForEquality(str As String, cmp As StringComparison) As Boolean
Dim position As Integer = str.IndexOf("://")
If position < 0 Then Return False
Dim substring As String = str.Substring(0, position)
Return substring.Equals("FILE", cmp)
End Function
End Module
' The example displays the following output:
' Culture-sensitive test for equality:
' Access to file://c:/notes.txt is allowed.
'
' Ordinal test for equality:
' Access to file://c:/notes.txt is not allowed.
нормализация
Некоторые символы Юникода имеют несколько представлений. Например, любой из следующих кодовых точек может представлять букву ắ:
- U+1EAF
- U+0103 U+0301
- U+0061 U+0306 U+0301
Несколько представлений для одного символа усложняют поиск, сортировку, сопоставление и другие строковые операции.
Стандарт Юникода определяет процесс, называемый нормализацией, который возвращает одно двоичное представление символа Юникода для любого из его эквивалентных двоичных представлений. Нормализация может использовать несколько алгоритмов, называемых формами нормализации, которые соответствуют разным правилам. .NET поддерживает формы нормализации Юникода C, D, KC и KD. Если строки нормализованы в той же форме нормализации, их можно сравнить с помощью порядкового сравнения.
Порядковое сравнение — это двоичное сравнение скалярного значения Юникода соответствующих Char объектов в каждой строке. Класс String включает ряд методов, которые могут выполнять порядковое сравнение, в том числе следующие:
Любая перегрузка методов Compare, Equals, StartsWith, EndsWith, IndexOf и LastIndexOf, которые включают параметр StringComparison. Метод выполняет порядковое сравнение, если задано значение StringComparison.Ordinal или OrdinalIgnoreCase для этого параметра.
Перегрузки CompareOrdinal метода.
Методы, использующие порядковое сравнение по умолчанию, например Contains, Replaceи Split.
Методы, которые ищут Char значение или элементы массива Char в экземпляре строки. К таким методам относятся IndexOf(Char) и Split(Char[]).
Вы можете определить, нормализуется ли строка для нормализации формы C путем вызова String.IsNormalized() метода или вызвать String.IsNormalized(NormalizationForm) метод, чтобы определить, нормализуется ли строка в указанной форме нормализации. Можно также вызвать String.Normalize() метод для преобразования строки в форму нормализации C или вызвать String.Normalize(NormalizationForm) метод для преобразования строки в указанную форму нормализации. Пошаговую информацию о нормализации и сравнении строк можно найти в методах Normalize() и Normalize(NormalizationForm).
В следующем простом примере показана нормализация строк. Он определяет букву "ố" тремя разными способами в трех разных строках и использует порядковое сравнение для равенства, чтобы определить, что каждая строка отличается от других двух строк. Затем он преобразует каждую строку в поддерживаемые формы нормализации и снова выполняет порядковое сравнение каждой строки в указанной форме нормализации. В каждом случае второй тест на равенство показывает, что строки равны.
using System;
using System.Globalization;
using System.IO;
using System.Text;
public class Example13
{
private static StreamWriter sw;
public static void Main()
{
sw = new StreamWriter(@".\TestNorm1.txt");
// Define three versions of the same word.
string s1 = "sống"; // create word with U+1ED1
string s2 = "s\u00F4\u0301ng";
string s3 = "so\u0302\u0301ng";
TestForEquality(s1, s2, s3);
sw.WriteLine();
// Normalize and compare strings using each normalization form.
foreach (string formName in Enum.GetNames(typeof(NormalizationForm)))
{
sw.WriteLine("Normalization {0}:\n", formName);
NormalizationForm nf = (NormalizationForm) Enum.Parse(typeof(NormalizationForm), formName);
string[] sn = NormalizeStrings(nf, s1, s2, s3);
TestForEquality(sn);
sw.WriteLine("\n");
}
sw.Close();
}
private static void TestForEquality(params string[] words)
{
for (int ctr = 0; ctr <= words.Length - 2; ctr++)
for (int ctr2 = ctr + 1; ctr2 <= words.Length - 1; ctr2++)
sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
words[ctr], ShowBytes(words[ctr]),
words[ctr2], ShowBytes(words[ctr2]),
words[ctr].Equals(words[ctr2], StringComparison.Ordinal));
}
private static string ShowBytes(string str)
{
string result = null;
foreach (var ch in str)
result += $"{(ushort)ch:X4} ";
return result.Trim();
}
private static string[] NormalizeStrings(NormalizationForm nf, params string[] words)
{
for (int ctr = 0; ctr < words.Length; ctr++)
if (! words[ctr].IsNormalized(nf))
words[ctr] = words[ctr].Normalize(nf);
return words;
}
}
// The example displays the following output:
// sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
// sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
// sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//
// Normalization FormC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//
//
// Normalization FormKC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormKD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
open System
open System.IO
open System.Text
do
use sw = new StreamWriter(@".\TestNorm1.txt")
let showBytes (str: string) =
let mutable result = ""
for ch in str do
result <- result + $"{uint16 ch:X4} "
result.Trim()
let testForEquality (words: string[]) =
for ctr = 0 to words.Length - 2 do
for ctr2 = ctr + 1 to words.Length - 1 do
sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
words[ctr], showBytes(words[ctr]),
words[ctr2], showBytes(words[ctr2]),
words[ctr].Equals(words[ctr2], StringComparison.Ordinal))
let normalizeStrings nf (words: string[]) =
for i = 0 to words.Length - 1 do
if not (words[i].IsNormalized nf) then
words[i] <- words[i].Normalize nf
words
// Define three versions of the same word.
let s1 = "sống" // create word with U+1ED1
let s2 = "s\u00F4\u0301ng"
let s3 = "so\u0302\u0301ng"
testForEquality [| s1; s2; s3 |]
sw.WriteLine()
// Normalize and compare strings using each normalization form.
for formName in Enum.GetNames typeof<NormalizationForm> do
sw.WriteLine("Normalization {0}:\n", formName)
let nf = Enum.Parse(typeof<NormalizationForm>, formName) :?> NormalizationForm
let sn = normalizeStrings nf [| s1; s2; s3|]
testForEquality sn
sw.WriteLine "\n"
// The example displays the following output:
// sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
// sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
// sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//
// Normalization FormC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//
//
// Normalization FormKC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormKD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
Imports System.Globalization
Imports System.IO
Imports System.Text
Module Example16
Private sw As StreamWriter
Public Sub Main()
sw = New StreamWriter(".\TestNorm1.txt")
' Define three versions of the same word.
Dim s1 As String = "sống" ' create word with U+1ED1
Dim s2 As String = "s" + ChrW(&HF4) + ChrW(&H301) + "ng"
Dim s3 As String = "so" + ChrW(&H302) + ChrW(&H301) + "ng"
TestForEquality(s1, s2, s3)
sw.WriteLine()
' Normalize and compare strings using each normalization form.
For Each formName In [Enum].GetNames(GetType(NormalizationForm))
sw.WriteLine("Normalization {0}:", formName)
Dim nf As NormalizationForm = CType([Enum].Parse(GetType(NormalizationForm), formName),
NormalizationForm)
Dim sn() As String = NormalizeStrings(nf, s1, s2, s3)
TestForEquality(sn)
sw.WriteLine(vbCrLf)
Next
sw.Close()
End Sub
Private Sub TestForEquality(ParamArray words As String())
For ctr As Integer = 0 To words.Length - 2
For ctr2 As Integer = ctr + 1 To words.Length - 1
sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
words(ctr), ShowBytes(words(ctr)),
words(ctr2), ShowBytes(words(ctr2)),
words(ctr).Equals(words(ctr2), StringComparison.Ordinal))
Next
Next
End Sub
Private Function ShowBytes(str As String) As String
Dim result As String = Nothing
For Each ch In str
result += String.Format("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Return result.Trim()
End Function
Private Function NormalizeStrings(nf As NormalizationForm, ParamArray words() As String) As String()
For ctr As Integer = 0 To words.Length - 1
If Not words(ctr).IsNormalized(nf) Then
words(ctr) = words(ctr).Normalize(nf)
End If
Next
Return words
End Function
End Module
' The example displays the following output:
' sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
' sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
' sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
'
' Normalization FormC:
'
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'
'
' Normalization FormD:
'
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'
'
' Normalization FormKC:
'
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'
'
' Normalization FormKD:
'
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
Дополнительные сведения о нормализации и формах нормализации см. также System.Text.NormalizationForm, а также Юникод стандартное приложение №15: Формы нормализации и Часто задаваемые вопросы о нормализации на веб-сайте unicode.org.
Строковые операции по категориям
Класс String предоставляет элементы для сравнения строк, тестирования строк для равенства, поиска символов или подстроок в строке, изменения строки, извлечения подстроок из строки, объединения строк, форматирования значений, копирования строки и нормализации строки.
Сравнение строк
Строки можно сравнить, чтобы определить их относительную позицию в порядке сортировки с помощью следующих String методов:
Compare возвращает целое число, указывающее связь одной строки со второй строкой в порядке сортировки.
CompareOrdinal возвращает целое число, указывающее связь одной строки со второй строкой на основе сравнения точек кода.
CompareTo возвращает целое число, указывающее связь текущего экземпляра строки со второй строкой в порядке сортировки. Этот CompareTo(String) метод предоставляет IComparable и IComparable<T> реализации для String класса.
Проверка строк на равенство
Вызывается Equals метод, чтобы определить, равны ли две строки. Экземпляр Equals(String, String, StringComparison) и статические Equals(String, StringComparison) перегрузки позволяют указать, учитывается ли сравнение с учетом языка и региональных параметров или порядковый номер, а также учитывается ли случай или игнорируется. Большинство тестов на равенство являются порядковыми, и сравнения на равенство, определяющие доступ к системному ресурсу (например, объекту файловой системы), должны всегда быть порядковыми.
Поиск символов в строке
Класс String включает два типа методов поиска:
Методы, возвращающие значение, указывающее, присутствует ли определенная Boolean подстрока в строковом экземпляре. К ним относятся методы Contains, EndsWith и StartsWith.
Методы, указывающие начальную позицию подстроки в строковом экземпляре. К ним относятся методы IndexOf, IndexOfAny, LastIndexOf и LastIndexOfAny.
Предупреждение
Если вы хотите выполнить поиск строки для определенного шаблона, а не определенной подстроки, следует использовать регулярные выражения. Дополнительные сведения см. в разделе .NET Регулярные выражения.
Изменение строки
Класс String включает следующие методы, которые, как представляется, изменяют значение строки:
PadLeft вставляет один или несколько вхождений указанного символа в начало строки.
PadRight вставляет одно или несколько вхождений указанного символа в конце строки.
Replace заменяет подстроку другой подстрокой в текущем String экземпляре.
ToLower и ToLowerInvariant преобразуйте все символы в строке в нижний регистр.
ToUpper и ToUpperInvariant преобразуют все символы в строке в верхний регистр.
Trim Функция удаляет все вхождения символов из начала и конца строки.
TrimEnd Удаляет все вхождения символа с конца строки.
TrimStart Удаляет все вхождения символа из начала строки.
Внимание
Все методы изменения строки возвращают новый String объект. Они не изменяют значение текущего экземпляра.
Извлечение подстрок из строки
Метод String.Split разделяет одну строку на несколько строк. Перегрузки метода позволяют указать несколько разделителей, ограничить количество подстроок, извлекаемых методом, обрезать пробелы из подстроок и указать, включаются ли пустые строки (которые происходят при смежных разделителях) среди возвращаемых строк.
Объединение строк
Для объединения строк можно использовать следующие String методы:
- Concat объединяет одну или несколько подстроок в одну строку.
- Join объединяет одну или несколько подстроок в один элемент и добавляет разделитель между каждой подстрокой.
Форматирование значений
Метод String.Format использует составную функцию форматирования для замены одного или нескольких заполнителей в строке строковым представлением какого-либо объекта или значения. Метод Format часто используется для выполнения следующих действий:
- Чтобы включить строковое представление числового значения в строку.
- Чтобы внедрить строковое представление значения даты и времени в строку.
- Чтобы внедрить строковое представление значения перечисления в строку.
- Чтобы внедрить строковое представление какого-то объекта, поддерживающего IFormattable интерфейс в строке.
- Для выравнивания подстроки по правому или левому краю в пределах большей строки.
Подробные сведения о операциях форматирования и примерах см. в сводке по перегрузке Format .
Копировать строку
Чтобы создать копию строки, можно вызвать следующие String методы:
- Clone возвращает ссылку на существующий String объект.
- CopyTo копирует часть строки в массив символов.
Нормализация строки
В Юникоде один символ может иметь несколько точек кода. Нормализация преобразует эти эквивалентные символы в то же двоичное представление. Метод String.Normalize выполняет нормализацию, а String.IsNormalized метод определяет, нормализуется ли строка.
Дополнительные сведения и пример см. в разделе нормализации, приведенном ранее в этой статье.