Экспорт (0) Печать
Развернуть все
Данная статья переведена автоматически. Наведите указатель мыши на предложения статьи, чтобы просмотреть исходный текст. Дополнительные сведения.
Перевод
Текст оригинала

Рекомендации по использованию строк в .NET Framework

Платформа .NET Framework предоставляет расширенную поддержку разработки локализованных и глобализованных приложений, а также упрощает применение правил текущего или указанного языка и региональных параметров при выполнении таких распространенных операций, как сортировка строк и их отображение. Но операция по сортировке или отображению строк не всегда зависит от языка и региональных параметров. Например, строки, предназначенные для внутреннего использования приложением, обычно обрабатываются одинаково для всех языков и региональных параметров. Если независимые от языка и региональных параметров строковые данные, такие как XML-теги, HTML-теги, имена пользователей, пути файлов и имена системных объектов, интерпретируются как зависимые от языка и региональных параметров, то это может привести к трудно выявляемым ошибкам кода приложения, уменьшению производительности и в некоторых случаях к проблемам с безопасностью.

В данной теме рассматриваются методы сортировки, сравнения и использования прописных и строчных букв в .NET Framework и последующих версиях, даются рекомендации по выбору подходящего метода обработки строк и предоставляются дополнительные сведения об этих методах. Также проверяется, как форматированные данные, такие как числовые типы данных и данные даты и времени, настраиваются для отображения и хранения.

Этот раздел содержит следующие подразделы.

При разработке на платформе .NET Framework необходимо следовать приведенным далее рекомендациям по использованию строк.

  • Следует использовать перегрузки, которые явно задают правила сравнения строк для операций со строками. Обычно это подразумевает вызов перегрузки метода с параметром типа StringComparison.

  • В качестве безопасных значений по умолчанию для независимого от языка и региональных параметров сопоставления строк следует использовать для сравнений StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase.

  • Для повышения производительности следует использовать сравнения с StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase.

  • При отображении выходных результатов для пользователя следует применять операции со строками на основе StringComparison.CurrentCulture.

  • Для сравнения не лингвистических данных (например символьных) следует использовать не лингвистические значения StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase вместо операций со строками на основе CultureInfo.InvariantCulture.

  • При нормализации строк для сравнения следует использовать метод String.ToUpperInvariant вместо метода String.ToLowerInvariant.

  • Для проверки, являются ли две строки равными, следует использовать перегрузку метода String.Equals.

  • Методы String.Compare и String.CompareTo следует использовать для сортировки строк, а не для проверки на равенство.

  • Используйте форматирование, учитывающие региональные параметры, для отображения нестроковых данных таких, как числа и даты, в пользовательском интерфейсе. Используйте форматирование с инвариантным региональными параметрами для сохранения нестроковых данных в строковом виде.

При использовании строк следует избегать применения приведенных далее методик.

  • Не следует использовать перегрузки, которые не задают явно или неявно правила сравнения строк для операций со строками.

  • В большинстве случаев не следует использовать операции со строками на основе StringComparison.InvariantCulture. Одно из немногих исключений – случай, когда сохраняются лингвистически значимые, но независимые от языка и региональных параметров данные.

  • Не следует использовать перегрузку метода String.Compare или CompareTo и проверку, равно ли возвращаемое значение 0, для определения равенства двух строк.

  • Не используйте форматирование, учитывающие региональные параметры, для сохранения числовых данных и данных даты и времени в строковом виде.

К началу

Большинство методов обработки строк платформы .NET Framework являются перегруженными. Обычно одни перегрузки принимают параметры по умолчанию, а другие не принимают и вместо перегрузок определяют точный путь сравнения или обработки строк. Большинство методов, не принимающих параметры по умолчанию, включают параметр типа StringComparison, являющийся перечислением, явно задающим правила сравнения строк в зависимости от языка и региональных параметров, а также регистра. В следующей таблице приводятся элементы перечисления StringComparison и их описания.

Элемент StringComparison

Описание

CurrentCulture

Выполняет сравнение с учетом регистра и с использованием текущего языка и региональных параметров.

CurrentCultureIgnoreCase

Выполняет сравнение без учета регистра и с использованием текущего языка и региональных параметров.

InvariantCulture

Выполняет сравнение с учетом регистра и с использованием инвариантного языка и региональных параметров.

InvariantCultureIgnoreCase

Выполняет сравнение без учета регистра и с использованием инвариантного языка и региональных параметров.

Ordinal

Выполняет порядковое сравнение.

OrdinalIgnoreCase

Выполняет порядковое сравнение без учета регистра.

Например, метод IndexOf, возвращающий индекс части строки в объекте String, соответствующей символу или строке, имеет девять перегрузок:

Рекомендуется выбирать перегрузку, не использующую значения по умолчанию, по приведенным далее причинам.

  • Некоторые перегрузки с параметрами по умолчанию (те, которые выполняют поиск Char в экземпляре строки) выполняют порядковое сравнение, в то время как другие (те, которые ищут строку в экземпляре строки) учитывают язык и региональные параметры. Поскольку трудно запомнить, какой метод использует какие значения по умолчанию, легко перепутать перегрузки.

  • Назначение кода, зависящего от значений по умолчанию для вызовов метода, не ясно. В следующем примере, зависящем от значений по умолчанию, трудно понять, что именно планировал разработчик – порядковое или лингвистическое сравнение двух строк, и может ли разный регистр protocol и "http" привести к тому, что проверка на равенство возвратит значение false.

    
    string protocol = GetProtocol(url);       
    if (String.Equals(protocol, "http")) {
       // ...Code to handle HTTP protocol.
    }
    else {
       throw new InvalidOperationException();
    }
    
    
    

В целом рекомендуется вызывать метод, не зависящий от значений по умолчанию, поскольку они приводят к неоднозначности кода. В свою очередь использование методов, не зависящих от значений по умолчанию, делает код более читаемым и упрощает его отладку и обслуживание. В следующем примере рассматриваются вопросы, возникшие в процессе рассмотрения предыдущего примера. Он явно демонстрирует, что используется порядковое сравнение и что различия в регистрах символов игнорируются.


string protocol = GetProtocol(url);       
if (String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase)) {
   // ...Code to handle HTTP protocol.
}
else {
   throw new InvalidOperationException();
}


К началу

Сравнение строк является основой большинства операций со строками, в частности сортировки и проверки на равенство. Строки сортируются в определенном порядке: если в упорядоченном списке строк "my" появляется до "string", то "my" должна сравниваться не больше "string". Кроме того, сравнение неявно задает равенство. Операция сравнения возвращает 0 для строк, которые она сочла равными. Правильная интерпретация этого – ни одна из строк не меньше другой. Наиболее значимые операции со строками включают одну или обе процедуры: сравнение с другой строкой и выполнение правильно определенной операции сортировки.

Однако оценка равенства двух строк или порядка сортировки не гарантирует единственный правильный результат; результат зависит от условия, которое использовалось для сравнения строк. В частности, порядковые сравнения строк и сравнения на основе правил использования прописных и строчных букв и сортировки текущих языка и региональных параметров или инвариантных (независимых от языковых стандартов языка и региональных параметров на основе английского языка) могут приводить к разным результатам.

Dd465121.collapse_all(ru-ru,VS.110).gifСравнения строк, использующие текущие язык и региональные параметры

Один критерий включает использование при сравнении строк правил текущего языка и региональных параметров. Сравнения на основе текущего языка и региональных параметров используют текущий язык и региональные параметры или языковый стандарт потока. Если язык и региональные параметры не заданы пользователем, то по умолчанию принимается значение, установленное в окне Региональные параметры панели управления. Следует всегда использовать сравнения на основе текущего языка и региональных параметров, если сравниваются лингвистические данные, которые отражают взаимодействие с пользователем, учитывающее язык и региональные параметры.

Однако при изменении языка и региональных параметров поведение сравнения и правил использования прописных и строчных букв в .NET Framework изменяется. Это происходит, когда приложение выполняется на компьютере с языком и региональными параметрами, отличными от тех, которые имелись на компьютере разработки приложения, или когда поток выполнения изменяет язык и региональные параметры. Это преднамеренное поведение, но неочевидное для многих разработчиков. Следующий пример иллюстрирует различия в порядке сортировки между языком и региональными параметрами "Английский (США)" и языком и региональными параметрами "Шведский (Швеция)". Обратите внимание, что слова "ångström", "Windows" и "Visual Studio" появляются в упорядоченных массивах строк в разных позициях.


using System;
using System.Globalization;
using System.Threading;

public class Example
{
   public static void Main()
   {
      string[] values= { "able", "ångström", "apple", "Æble", 
                         "Windows", "Visual Studio" };
      Array.Sort(values);
      DisplayArray(values);

      // Change culture to Swedish (Sweden).
      string originalCulture = CultureInfo.CurrentCulture.Name;
      Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
      Array.Sort(values);
      DisplayArray(values);

      // Restore the original culture.
      Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);
    }

    private static void DisplayArray(string[] values)
    {
      Console.WriteLine("Sorting using the {0} culture:",  
                        CultureInfo.CurrentCulture.Name);
      foreach (string value in values)
         Console.WriteLine("   {0}", value);

      Console.WriteLine();
    }
}
// The example displays the following output:
//       Sorting using the en-US culture:
//          able
//          Æble
//          ångström
//          apple
//          Visual Studio
//          Windows
//       
//       Sorting using the sv-SE culture:
//          able
//          Æble
//          apple
//          Windows
//          Visual Studio
//          ångström


Учитывающие регистр сравнения, использующие текущий язык и региональные параметры, совпадают со сравнениями, учитывающими язык и региональные параметры, за исключением того, что в первых сравнениях не учитывается регистр, как того требуют текущие язык и региональные параметры потока. Это поведение может также самостоятельно проявляться в порядках сортировки.

Сравнения, использующие семантику текущего языка и региональных параметров, являются сравнениями по умолчанию для следующих методов:

В любом случае рекомендуется вызвать перегрузку, имеющую параметр StringComparison, чтобы сделать ясным смысл вызова метода.

При лингвистической интерпретации нелингвистических строковых данных или при интерпретации строковых данных определенного языка и региональных параметров с использованием правил другого языка и региональных параметров могут возникать трудно и не очень трудно выявляемые ошибки. Типичный пример – проблема с турецкой буквой I.

Почти во всех латинских алфавитах, включая английский (США), символ "i" (\u0069) — это строчная версия символа "I" (\u0049). Это правило использования прописных и строчных букв стало само собой разумеющимся для тех, кто программирует для таких языков. Однако в турецком ("tr-TR") алфавите имеется буква "I с точкой", "İ" (\u0130), которая является прописной версией буквы "i". В турецком языке также имеется символ "i без точки", буква "ı" (\u0131), которая является строчной версией прописной буквы "I". Эта же особенность имеется и в азербайджанском языке ("az").

Следовательно, предположения о прописной "i" или строчной "I" не являются правильными для всех языков. При использовании перегрузок по умолчанию для процедур сравнения строк они будут меняться в зависимости от языка и региональных параметров. Если сравниваемые данные не являются лингвистическими, использование перегрузок по умолчанию может привести к нежелательным результатам, как показывает следующая попытка выполнения сравнения строк "file" и "FILE" без учета регистра.


using System;
using System.Globalization;
using System.Threading;

public class Example
{
   public static void Main()
   {
      string fileUrl = "file";
      Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
      Console.WriteLine("Culture = {0}",
                        Thread.CurrentThread.CurrentCulture.DisplayName);
      Console.WriteLine("(file == FILE) = {0}", 
                       fileUrl.StartsWith("FILE", true, null));
      Console.WriteLine();

      Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
      Console.WriteLine("Culture = {0}",
                        Thread.CurrentThread.CurrentCulture.DisplayName);
      Console.WriteLine("(file == FILE) = {0}", 
                        fileUrl.StartsWith("FILE", true, null));
   }
}
// The example displays the following output:
//       Culture = English (United States)
//       (file == FILE) = True
//       
//       Culture = Turkish (Turkey)
//       (file == FILE) = False


Это сравнение может вызвать значительные проблемы, если язык и региональные параметры случайно использовались в важных для обеспечения безопасности параметрах, как показано в следующем примере. Вызов метода, такого как IsFileURI("file:"), возвращает значение true, если текущий язык и региональные параметры — английский (США), и значение false текущий язык и региональные параметры — турецкий. Следовательно, в турецких системах кто-нибудь может обойти меры безопасности, блокирующие доступ к независящим от регистра универсальным кодам ресурсов (URI), начинающимся с "FILE:".


public static bool IsFileURI(String path) 
{
   return path.StartsWith("FILE:", true, null);
}


В этом случае, поскольку "file:" положено интерпретировать как не лингвистический и не зависящий от языка и региональных параметров идентификатор, должен быть написан другой код, показанный в следующем примере.


public static bool IsFileURI(string path) 
{
   return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
}


Dd465121.collapse_all(ru-ru,VS.110).gifОперации по порядковому сравнению строк

Указание значения StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase в вызове метода означает нелингвистическое сравнение, при котором особенности национальных языков не учитываются. Методы, которые вызываются с этими значениями StringComparison, строят операции сравнения на простых побайтовых сравнениях вместо правил использования прописных и строчных букв или таблиц эквивалентности, параметризованных по языку и региональным параметрам. В большинстве случаев такой подход наилучшим образом соответствует предполагаемой интерпретации строк, делая код более быстрым и более читаемым.

Порядковые сравнения – это сравнения строк, при которых все байты каждой строки сравниваются без лингвистической интерпретации; например "windows" не соответствует "Windows". В сущности, это вызов функции strcmp среды выполнения C. Следует использовать такое сравнение, когда контекст указывает, что строки должны точно совпадать, или требует консервативную политику соответствия. Кроме того, порядковое сравнение является самой быстрой операцией сравнения, поскольку в нем не применяются лингвистические правила для определения результата.

Строки в .NET Framework могут содержать включенные символы NULL. Одно из самых очевидных различий между порядковым сравнением и сравнением с учетом языка и региональных параметров (включая сравнения, использующие инвариантный язык и региональные параметры) заключается в обработке включенных в строку символов NULL. При использовании методов String.Compare и String.Equals для выполнения сравнений с учетом языка и региональных параметров (включая сравнения, использующие инвариантный язык и региональные параметры) эти символы игнорируются. В результате при сравнениях, учитывающих язык и региональные параметры, строки, содержащие символы NULL, могут считаться равными строкам без этих символов.

Важное примечание Важно

Хотя методы сравнения строк пропускают включенные символы NULL, методы поиска строк, такие как String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf и String.StartsWith, их не пропускают.

В следующем примере выполняется сравнение с учетом языка и региональных параметров строки "Aa" с аналогичной строкой, содержащей несколько символов NULL между "A" и "a", и показывается, каким образом эти строки рассматриваются как равные.


using System;

public class Example
{
   public static void Main()
   {
      string str1 = "Aa";
      string str2 = "A" + new String('\u0000', 3) + "a";
      Console.WriteLine("Comparing '{0}' ({1}) and '{2}' ({3}):", 
                        str1, ShowBytes(str1), str2, ShowBytes(str2));
      Console.WriteLine("   With String.Compare:");
      Console.WriteLine("      Current Culture: {0}", 
                        String.Compare(str1, str2, StringComparison.CurrentCulture));
      Console.WriteLine("      Invariant Culture: {0}", 
                        String.Compare(str1, str2, StringComparison.InvariantCulture));

      Console.WriteLine("   With String.Equals:");
      Console.WriteLine("      Current Culture: {0}", 
                        String.Equals(str1, str2, StringComparison.CurrentCulture));
      Console.WriteLine("      Invariant Culture: {0}", 
                        String.Equals(str1, str2, StringComparison.InvariantCulture));
   }

   private static string ShowBytes(string str)
   {
      string hexString = String.Empty;
      for (int ctr = 0; ctr < str.Length; ctr++)
      {
         string result = String.Empty;
         result = Convert.ToInt32(str[ctr]).ToString("X4");
         result = " " + result.Substring(0,2) + " " + result.Substring(2, 2);
         hexString += result;
      }
      return hexString.Trim();
   }
}
// The example displays the following output:
//    Comparing 'Aa' (00 41 00 61) and 'A   a' (00 41 00 00 00 00 00 00 00 61):
//       With String.Compare:
//          Current Culture: 0
//          Invariant Culture: 0
//       With String.Equals:
//          Current Culture: True
//          Invariant Culture: True


Однако при использовании порядкового сравнения эти строки не получатся равными, как показано в следующем примере.


Console.WriteLine("Comparing '{0}' ({1}) and '{2}' ({3}):", 
                  str1, ShowBytes(str1), str2, ShowBytes(str2));
Console.WriteLine("   With String.Compare:");
Console.WriteLine("      Ordinal: {0}", 
                  String.Compare(str1, str2, StringComparison.Ordinal));

Console.WriteLine("   With String.Equals:");
Console.WriteLine("      Ordinal: {0}", 
                  String.Equals(str1, str2, StringComparison.Ordinal));
// The example displays the following output:
//    Comparing 'Aa' (00 41 00 61) and 'A   a' (00 41 00 00 00 00 00 00 00 61):
//       With String.Compare:
//          Ordinal: 97
//       With String.Equals:
//          Ordinal: False


Порядковые сравнения без учета регистра являются следующим из наиболее консервативных подходов. В таких сравнениях игнорируется большая часть правил использования прописных и строчных букв; например "windows" соответствует "Windows". При работе с символами ASCII эта политика эквивалентна StringComparison.Ordinal, за исключением того, что игнорирует обычные правила использования прописных и строчных букв ASCII. Следовательно, любой символ в диапазоне [A, Z] (\u0041-\u005A) равен соответствующему символу в диапазоне [a, z] (\u0061-\007A). Правила использования прописных и строчных букв вне диапазона ASCII используют таблицы инвариантного языка и региональных параметров. Таким образом, сравнение


String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);


эквивалентно следующему сравнению (но гораздо быстрее):


String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), 
               StringComparison.Ordinal);


Оба эти сравнения довольно быстрые.

Примечание Примечание

Поведение строк файловой системы, разделов и значений реестра и переменных среды наилучшим образом представляется сравнением StringComparison.OrdinalIgnoreCase.

Оба сравнения, StringComparison.Ordinal и StringComparison.OrdinalIgnoreCase, используют напрямую двоичные значения и наиболее подходят для сопоставления. Если нет уверенности насчет параметров сравнения, следует использовать одно из этих двух значений. Однако поскольку они используют побайтовое сравнение, упорядочивание выполняется не в соответствии с лингвистическим порядком сортировки (как в английском словаре), а в соответствии с двоичным порядком сортировки. В большинстве контекстов результат может отображаться для пользователя с искажениями.

Порядковая семантика используется по умолчанию в перегрузках String.Equals, в которых отсутствует аргумент StringComparison (включая оператор равенства). В любом случае рекомендуется вызывать перегрузку, имеющую параметр StringComparison.

Dd465121.collapse_all(ru-ru,VS.110).gifОперации со строками, использующие инвариантный язык и региональные параметры

Сравнения с учетом инвариантного языка и региональных параметров используют свойство CompareInfo, возвращаемое статическим свойством CultureInfo.InvariantCulture. Это поведение одинаково во всех системах; оно преобразовывает любые символы вне собственного диапазона в те, которые с его точки зрения эквивалентны инвариантным символам. Эта политика может пригодиться для обслуживания одного набора поведения строк в нескольких средах языков и региональных параметров, но часто приводит к неожиданным результатам.

Сравнения без учета регистра и с учетом инвариантного языка и региональных параметров также используют для получения сведений о сравнении статическое свойство CompareInfo, возвращаемое статическим свойством CultureInfo.InvariantCulture. Любые различия регистров в этих преобразуемых символах игнорируются.

Сравнения. использующие StringComparison.InvariantCulture и StringComparison.Ordinal, работают со строками ASCII одинаково. Однако StringComparison.InvariantCulture принимает лингвистические решения, которые могут не подходить для строк, которые интерпретируются как набор байтов. Объект CultureInfo.InvariantCulture.CompareInfo создает метод Compare, который интерпретирует разные наборы символов как равные. Например, для инвариантного языка и региональных параметров верно следующее равенство:

InvariantCulture: a + ̊ = å

Символ "a" (\u0061), обозначающий СТРОЧНУЮ ЛАТИНСКУЮ БУКВУ A, находящийся рядом с символом ПРИСОЕДИНЕНИЯ ВЕРХНЕГО КРУЖКА "+ ̊" (\u030a), интерпретируется как символ СТРОЧНОЙ ЛАТИНСКОЙ БУКВЫ A С КРУЖКОМ ВВЕРХУ "å" (\u00e5). Как показано в следующем примере, это поведение отличается от поведения порядкового сравнения.


string separated = "\u0061\u030a";
string combined = "\u00e5";

Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture: {2}",
                  separated, combined, 
                  String.Compare(separated, combined, 
                                 StringComparison.InvariantCulture) == 0);

Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",
                  separated, combined,
                  String.Compare(separated, combined, 
                                 StringComparison.Ordinal) == 0);
// The example displays the following output:
//    Equal sort weight of a° and å using InvariantCulture: True
//    Equal sort weight of a° and å using Ordinal: False      


При интерпретации имен файлов, файлов Cookie или других объектов, в которых может встречаться такая комбинация, как "å", порядковые сравнения все еще предлагают наиболее прозрачное и подходящее поведение.

В итоге инвариантный язык и региональные параметры имеют очень мало свойств, которые делают их подходящими для использования при сравнении. Сравнение выполняется лингвистическим способом, что препятствует гарантии полного равенства символов, но это не является выбором для отображения в среде какого-либо языка и региональных параметров. Одной из немногих причин использования StringComparison.InvariantCulture для сравнения является наличие упорядоченных данных, что обеспечивает одинаковое отображение для всех языков и региональных параметров. Например, если к приложению прилагается большой файл данных, содержащий список упорядоченных идентификаторов для отображения, дополнение этого списка потребует вставку с сортировкой в инвариантном стиле.

К началу

В следующей таблице приведено сопоставление семантического контекста строк с элементом перечисления StringComparison.

Данные

Назначение

Соответствующее System.StringComparison

Value

Внутренние идентификаторы с учетом регистра.

Внутренние идентификаторы с учетом регистра в таких стандартах, как XML и HTTP.

Параметры, относящиеся к безопасности, с учетом регистра.

Не лингвистический идентификатор, в котором байты сопоставляются точно.

Ordinal

Внутренние идентификаторы без учета регистра.

Внутренние идентификаторы без учета регистра в таких стандартах, как XML и HTTP.

Пути файлов.

Разделы и значения реестра.

Переменные среды.

Идентификаторы ресурсов (например имена дескрипторов).

Параметры, относящиеся к безопасности, без учета регистра.

Не лингвистические идентификаторы, в которых регистр не учитывается; в частности данные, хранящиеся в большинстве системных служб Windows.

OrdinalIgnoreCase

Некоторые хранящиеся лингвистические данные.

Отображение лингвистических данных, требующее фиксированный порядок сортировки.

Данные, не зависящие от языка и региональных параметров, но являющиеся лингвистическими.

InvariantCulture

– или –

InvariantCultureIgnoreCase

Отображаемые для пользователя данные.

Большая часть ввода пользователя.

Данные, которым необходимы локальные лингвистические настройки.

CurrentCulture

– или –

CurrentCultureIgnoreCase

К началу

В следующих разделах рассматриваются методы, наиболее часто используемые для сравнения строк.

Dd465121.collapse_all(ru-ru,VS.110).gifString.Compare

Интерпретация по умолчанию: StringComparison.CurrentCulture.

Поскольку операция является основной для интерпретации строк, все экземпляры этих вызовов методов необходимо проверить, чтобы определить, должны ли строки интерпретироваться в соответствии с текущим языком и региональными параметрами или отдельно от языка и региональных параметров (по символам). Обычно выбирается последнее, и тогда должно использоваться сравнение StringComparison.Ordinal.

Класс System.Globalization.CompareInfo, возвращаемый свойством CultureInfo.CompareInfo, также включает метод Compare, который предоставляет большое количество вариантов сопоставления (порядковое, без учета пробелов, без учета типа каны и т.п.) с помощью перечисления флагов CompareOptions.

Dd465121.collapse_all(ru-ru,VS.110).gifString.CompareTo

Интерпретация по умолчанию: StringComparison.CurrentCulture.

Этот метод в настоящий момент не предлагает перегрузку, задающую тип StringComparison. Обычно возможно преобразовать этот метод в рекомендованную форму String.Compare(String, String, StringComparison).

Типы, реализующие интерфейсы IComparable и IComparable<T>, реализуют этот метод. Поскольку в нем не предусмотрен параметр StringComparison, реализуемые типы часто дают пользователям возможность указать StringComparer в своем конструкторе. В следующем примере определяется класс FileName, чей конструктор включает параметр StringComparer. Затем этот объект StringComparer используется в методе FileName.CompareTo.


using System;

public class FileName : IComparable
{
   string fname;
   StringComparer comparer; 

   public FileName(string name, StringComparer comparer)
   {
      if (String.IsNullOrEmpty(name))
         throw new ArgumentNullException("name");

      this.fname = name;

      if (comparer != null)
         this.comparer = comparer;
      else
         this.comparer = StringComparer.OrdinalIgnoreCase;
   }

   public string Name
   {
      get { return fname; }
   }

   public int CompareTo(object obj)
   {
      if (obj == null) return 1;

      if (! (obj is FileName))
         return comparer.Compare(this.fname, obj.ToString());
      else
         return comparer.Compare(this.fname, ((FileName) obj).Name);
   }
}


Dd465121.collapse_all(ru-ru,VS.110).gifString.Equals

Интерпретация по умолчанию: StringComparison.Ordinal.

Класс String предоставляет возможность проверки на равенство путем вызова перегруженного статического метода или перегруженного метода экземпляра Equals. По умолчанию перегрузки и оператор используют порядковое сравнение. Однако рекомендуется вызывать перегрузку, явно задающую тип StringComparison, даже если планируется выполнять порядковое сравнение; это облегчит поиск кода для разных интерпретаций строк.

Dd465121.collapse_all(ru-ru,VS.110).gifString.ToUpper и String.ToLower

Интерпретация по умолчанию: StringComparison.CurrentCulture.

Следует с осторожностью использовать эти методы, поскольку преобразование строки в верхний или нижний регистр часто используется в качестве небольшой нормализации для сравнения строк независимо от их регистра. В таком случае следует подумать об использовании сравнения без учета регистра.

Возможно также использование методов String.ToUpperInvariant и String.ToLowerInvariant. Метод ToUpperInvariant – это стандартный способ нормализации регистра. Сравнения, которые делаются с помощью StringComparison.OrdinalIgnoreCase, поведенчески являются объединением двух вызовов: вызова метода ToUpperInvariant для обоих строковых аргументов и сравнения с помощью метода StringComparison.Ordinal.

Перегрузки также возможно использовать для преобразования в верхний и нижний регистр в среде конкретного языка и региональных параметров путем передачи в метод объекта CultureInfo, представляющего этот язык и региональные параметры.

Dd465121.collapse_all(ru-ru,VS.110).gifChar.ToUpper и Char.ToLower

Интерпретация по умолчанию: StringComparison.CurrentCulture.

Эти методы работают аналогично методам String.ToUpper и String.ToLower, которые рассматривались в предыдущем разделе.

Dd465121.collapse_all(ru-ru,VS.110).gifString.StartsWith и String.EndsWith

Интерпретация по умолчанию: StringComparison.CurrentCulture.

По умолчанию оба этих метода выполняют сравнение с учетом языка и региональных параметров.

Dd465121.collapse_all(ru-ru,VS.110).gifString.IndexOf и String.LastIndexOf

Интерпретация по умолчанию: StringComparison.CurrentCulture.

Имеется некоторая непоследовательность в том, как перегрузки этих методов по умолчанию выполняют сравнения. Все методы String.IndexOf и String.LastIndexOf, включающие параметр Char, выполняют порядковое сравнение, но методы по умолчанию String.IndexOf и String.LastIndexOf, включающие параметр String, выполняют сравнение с учетом языка и региональных параметров.

При вызове метода String.IndexOf(String) или String.LastIndexOf(String) и передаче его в строку для обнаружения в текущем экземпляре рекомендуется вызывать перегрузку, которая явно указывает тип StringComparison. Перегрузки, включающие аргумент Char, не разрешают указывать тип StringComparison.

К началу

Некоторые не строковые методы, основной операцией которых является сравнение строк, используют тип StringComparer. Класс StringComparer включает шесть статических свойств, возвращающих экземпляры StringComparer, методы StringComparer.Compare которых выполняют следующие типы сравнений строк:

  • Сравнения строк с учетом языка и региональных параметров, использующие текущий язык и региональные параметры. Этот объект StringComparer возвращается свойством StringComparer.CurrentCulture.

  • Сравнения без учета регистра и с использованием текущего языка и региональных параметров. Этот объект StringComparer возвращается свойством StringComparer.CurrentCultureIgnoreCase.

  • Сравнения без учета языка и региональных параметров, использующие правила сравнения слов инвариантного языка и региональных параметров. Этот объект StringComparer возвращается свойством StringComparer.InvariantCulture.

  • Сравнения без учета регистра, языка и региональных параметров, использующие правила сравнения слов инвариантного языка и региональных параметров. Этот объект StringComparer возвращается свойством StringComparer.InvariantCultureIgnoreCase.

  • Порядковое сравнение. Этот объект StringComparer возвращается свойством StringComparer.Ordinal.

  • Порядковое сравнение без учета регистра. Этот объект StringComparer возвращается свойством StringComparer.OrdinalIgnoreCase.

Dd465121.collapse_all(ru-ru,VS.110).gifArray.Sort и Array.BinarySearch

Интерпретация по умолчанию: StringComparison.CurrentCulture.

При сохранении каких-либо данных в коллекции или считывании существующих данных из файла или базы данных в коллекцию переключение текущего языка и региональных параметров может сделать инварианты в коллекции недействительными. Метод Array.BinarySearch предполагает, что элементы массива, которые следует искать, уже отсортированы. Для сортировки каких-либо строковых элементов в массиве метод Array.Sort вызывает метод String.Compare для упорядочивания отдельных элементов. Использование механизма сравнения, учитывающего язык и региональные параметры, может быть опасным, если в промежутке между сортировкой массива и поиском по его содержимому язык и региональные параметры изменятся. Например, в следующем коде операции хранения и извлечения работают с механизмом сравнения, который неявно предоставляется свойством Thread.CurrentThread.CurrentCulture. Если может произойти изменение языка и региональных параметров между вызовами StoreNames и DoesNameExist, и особенно если содержимое массива постоянно присутствует где-нибудь еще между этими двумя вызовами методов, двоичный поиск может завершиться неудачно.


// Incorrect.
string []storedNames;

public void StoreNames(string [] names)
{
   int index = 0;
   storedNames = new string[names.Length];

   foreach (string name in names)
   {
      this.storedNames[index++] = name;
   }

   Array.Sort(names); // Line A.
}

public bool DoesNameExist(string name)
{
   return (Array.BinarySearch(this.storedNames, name) >= 0);  // Line B.
}


Рекомендуемый вариант показан в следующем примере, в котором для сортировки массива и для поиска в нем используется одно и то же порядковое (не учитывающее язык и региональные параметры) сравнение. Изменения кода показаны в строках с метками Line A и Line B в этих двух примерах.


// Correct.
string []storedNames;

public void StoreNames(string [] names)
{
   int index = 0;
   storedNames = new string[names.Length];

   foreach (string name in names)
   {
      this.storedNames[index++] = name;
   }

   Array.Sort(names, StringComparer.Ordinal);  // Line A.
}

public bool DoesNameExist(string name)
{
   return (Array.BinarySearch(this.storedNames, name, StringComparer.Ordinal) >= 0);  // Line B.
}


Если эти данные существуют и перемещаются в разных средах языка и региональных параметров, и для представления данных пользователю применяется сортировка, следует подумать об использовании сравнения StringComparison.InvariantCulture, которое работает лингвистически для улучшения представления данных пользователю, но не подвержено влиянию смены языка и региональных параметров. В следующем примере показано, как изменить два предыдущих примера и использовать для сортировки массива и поиска по нему инвариантный язык и региональные параметры.


// Correct.
string []storedNames;

public void StoreNames(string [] names)
{
   int index = 0;
   storedNames = new string[names.Length];

   foreach (string name in names)
   {
      this.storedNames[index++] = name;
   }

   Array.Sort(names, StringComparer.InvariantCulture);  // Line A.
}

public bool DoesNameExist(string name)
{
   return (Array.BinarySearch(this.storedNames, name, StringComparer.InvariantCulture) >= 0);  // Line B.
}


Dd465121.collapse_all(ru-ru,VS.110).gifПример коллекций. Конструктор таблицы хэширования

Хэшированные строки обеспечивают второй пример операции, на которую влияет способ сравнения строк.

В следующем примере объект Hashtable создается путем передачи его в объект StringComparer, возвращаемый свойством StringComparer.OrdinalIgnoreCase. Поскольку класс StringComparer, производный от StringComparer, реализует интерфейс IEqualityComparer, его метод GetHashCode используется для вычисления хэш-кода строк в хэш-таблице.


const int initialTableCapacity = 100;
Hashtable h;

public void PopulateFileTable(string directory)
{
   h = new Hashtable(initialTableCapacity, 
                     StringComparer.OrdinalIgnoreCase);

   foreach (string file in Directory.GetFiles(directory))
         h.Add(file, File.GetCreationTime(file));
}

public void PrintCreationTime(string targetFile)
{
   Object dt = h[targetFile];
   if (dt != null)
   {
      Console.WriteLine("File {0} was created at time {1}.",
         targetFile, 
         (DateTime) dt);
   }
   else
   {
      Console.WriteLine("File {0} does not exist.", targetFile);
   }
}


К началу

При отображении пользователям нестроковых данных таких, как числа, даты и время, форматируйте их с помощью пользовательских региональных параметров. По умолчанию метод String.Format и методы ToString числовых типов и типов даты и времени, используют текущие региональные параметры потока для операций форматирования. Чтобы явно указать, что метод форматирования должен использовать текущие региональные параметры, можно вызвать перегрузку метода форматирования, которая принимает параметр provider, такой как String.Format(IFormatProvider, String, Object[]) или DateTime.ToString(IFormatProvider), и передать ему свойство CultureInfo.CurrentCulture.

Можно ввести нестроковые данные либо в качестве двоичных данных, либо в качестве форматированных данных. При выборе сохранения в виде форматированных данных, необходимо вызвать перегрузку метода форматирования, которая включает параметр provider и передать свойство CultureInfo.InvariantCulture. Инвариантный региональные параметры предоставляют согласованный формат для форматированных данных, который не зависит от региональных параметров и компьютера. Напротив, сохраненные данные, которое форматируются с помощью региональных параметров, отличных от инвариантных региональных параметров, имеют некоторые ограничения.

  • Данные должны быть неиспользуемыми, если они получены для системы, которая имеет отличные региональные параметры, или если текущая система изменила текущие региональные параметры и попытки для извлечения данных.

  • Свойства региональных параметров для определенного компьютера могут отличаться от стандартных значений. В любой момент пользователь может настроить параметры отображения, учитывающие региональные параметры. Поэтому форматированные данные, которые сохранены в системе, не могут быть доступными для чтения после того, как пользователь настроит региональные параметры. Переносимость форматированных данных между компьютерами, вероятнее всего, будет ещё более ограниченной.

  • Международные, регионарные или национальные стандарты, определяющие форматирование чисел или дат и времени, изменяются со временем, и эти изменения объединяются в обновления операционной системы Windows. При изменении соглашений форматирования, данные, которые были отформатированы с помощью предыдущих соглашений, могут стать недоступными для чтения.

В следующем примере демонстрируется ограниченная переносимость, являющейся результатом использования форматирования, зависящих от региональных параметров, для сохранения данных. Пример сохраняет массив значений даты и времени в файл. Они форматированы с помощью соглашений региональных параметров Соединенных Штатов. После того как приложение изменяет текущие региональные параметры потока на французские (Швейцария), оно пытается считать значения, сохраненные с помощью форматирования текущих региональных параметров. Попытка чтения двух элементов данных вызывает исключение FormatException, а массив дат теперь содержит два неверных элемента, которые равны MinValue.


using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;

public class Example
{
   private static string filename = @".\dates.dat";

   public static void Main()
   {
      DateTime[] dates = { new DateTime(1758, 5, 6, 21, 26, 0), 
                           new DateTime(1818, 5, 5, 7, 19, 0), 
                           new DateTime(1870, 4, 22, 23, 54, 0),  
                           new DateTime(1890, 9, 8, 6, 47, 0), 
                           new DateTime(1905, 2, 18, 15, 12, 0) }; 
      // Write the data to a file using the current culture.
      WriteData(dates);
      // Change the current culture.
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-CH");
      // Read the data using the current culture.
      DateTime[] newDates = ReadData();
      foreach (var newDate in newDates)
         Console.WriteLine(newDate.ToString("g"));
   }

   private static void WriteData(DateTime[] dates) 
   {
      StreamWriter sw = new StreamWriter(filename, false, Encoding.UTF8);    
      for (int ctr = 0; ctr < dates.Length; ctr++) {
         sw.Write("{0}", dates[ctr].ToString("g", CultureInfo.CurrentCulture));
         if (ctr < dates.Length - 1) sw.Write("|");   
      }      
      sw.Close();
   }

   private static DateTime[] ReadData() 
   {
      bool exceptionOccurred = false;

      // Read file contents as a single string, then split it.
      StreamReader sr = new StreamReader(filename, Encoding.UTF8);
      string output = sr.ReadToEnd();
      sr.Close();   

      string[] values = output.Split( new char[] { '|' } );
      DateTime[] newDates = new DateTime[values.Length]; 
      for (int ctr = 0; ctr < values.Length; ctr++) {
         try {
            newDates[ctr] = DateTime.Parse(values[ctr], CultureInfo.CurrentCulture);
         }
         catch (FormatException) {
            Console.WriteLine("Failed to parse {0}", values[ctr]);
            exceptionOccurred = true;
         }
      }      
      if (exceptionOccurred) Console.WriteLine();
      return newDates;
   }
}
// The example displays the following output:
//       Failed to parse 4/22/1870 11:54 PM
//       Failed to parse 2/18/1905 3:12 PM
//       
//       05.06.1758 21:26
//       05.05.1818 07:19
//       01.01.0001 00:00
//       09.08.1890 06:47
//       01.01.0001 00:00
//       01.01.0001 00:00


Однако, если заменить свойство CultureInfo.CurrentCulture на CultureInfo.InvariantCulture в вызовах DateTime.ToString(String, IFormatProvider) и DateTime.Parse(String, IFormatProvider), хранимая дата и данные времени являются успешно восстановленными, как показано ниже.


06.05.1758 21:26
05.05.1818 07:19
22.04.1870 23:54
08.09.1890 06:47
18.02.1905 15:12

К началу

Добавления сообщества

ДОБАВИТЬ
Показ:
© 2015 Microsoft