Práticas recomendadas para o uso de cadeias de caracteres na.NET Framework

A.NET Framework oferece suporte extenso para desenvolver aplicativos localizados e globalizados e torna mais fácil de aplicar as convenções de cultura atual ou de uma cultura específica ao executar as operações comuns como, por exemplo, classificar e exibir seqüências de caracteres. Mas, classificar ou comparar cadeias nem sempre é uma operação de cultura. Por exemplo, seqüências de caracteres que são usadas internamente por um aplicativo normalmente devem ser tratadas idêntico em todas as culturas. Quando os dados de seqüência culturalmente independentes, como, por exemplo, XML marcas HTML marcas, nomes de usuário, caminhos de arquivo e os nomes dos objetos do sistema, são interpretados como se fossem sensíveis à cultura, o código do aplicativo pode ser sujeito a erros sutis, baixo desempenho e, em alguns casos, os problemas de segurança.

Este tópico examina a seqüência de caracteres de classificação, comparação e métodos de maiúsculas e minúsculas na .NET Framework versão 4 e versões posteriores, apresenta recomendações para selecionar um método de manipulação de seqüência de caracteres apropriado e fornece informações adicionais sobre métodos de manipulação de seqüência de caracteres.

Este tópico contém as seções a seguir:

  • Recomendações para o uso de seqüência de caracteres

  • Especificando explicitamente de comparações de seqüência de caracteres

  • Os detalhes da comparação de seqüência de caracteres

  • Escolhendo o membro StringComparison para a chamada de método

  • Métodos comuns de comparação de seqüência de caracteres na.NET Framework

  • Métodos que executam a comparação de seqüência de caracteres indiretamente

Recomendações para o uso de seqüência de caracteres

Ao desenvolver com o.NET Framework, siga estas recomendações de simples, quando você usa strings:

Evite as seguintes práticas ao usar seqüências de caracteres:

  • Não use sobrecargas que explicitamente ou implicitamente, especifique as regras de comparação de seqüência de caracteres para operações de cadeia de caracteres.

  • Não use as operações de cadeia de caracteres com base em StringComparison.InvariantCulture na maioria dos casos. Uma das poucas exceções é quando são persistência a dados meios lingüisticamente significativa mas culturalmente independente.

  • Não use uma sobrecarga de String.Compare ou CompareTo método e teste para um valor de retorno de zero para determinar se duas seqüências são iguais.

Voltar ao topo

Especificando explicitamente de comparações de seqüência de caracteres

A maioria dos métodos de manipulação de seqüência na.NET Framework estão sobrecarregados. Normalmente, o sobrecargas de um ou mais aceitam as configurações padrão, enquanto outros não aceitam nenhum padrão e em vez disso, defina a maneira precisa, no qual seqüências de caracteres devem ser comparados ou manipulado. A maioria dos métodos que não dependem de padrões incluem um parâmetro do tipo StringComparison, que é uma enumeração que especifica explicitamente a regras de comparação de seqüência de caracteres por cultura e o caso. A tabela a seguir descreve o StringComparison os membros de enumeração.

Membro do StringComparison

Descrição

CurrentCulture

Executa uma comparação diferenciando maiúsculas de minúsculas, usando a cultura atual.

CurrentCultureIgnoreCase

Executa uma comparação diferenciando maiúsculas de minúsculas, usando a cultura atual.

InvariantCulture

Executa uma comparação diferenciando maiúsculas de minúsculas, usando a cultura invariável.

InvariantCultureIgnoreCase

Executa uma comparação diferenciando maiúsculas de minúsculas, usando a cultura invariável.

Ordinal

Executa uma comparação ordinal.

OrdinalIgnoreCase

Executa uma comparação ordinal maiúsculas de minúsculas.

Por exemplo, o IndexOf método, que retorna o índice de uma subseqüência de caracteres em um String objeto corresponde a um caractere ou uma seqüência de caracteres tem nove de sobrecargas:

Recomendamos que você selecione uma sobrecarga que não usa os valores padrão, pelos seguintes motivos:

  • Algumas sobrecargas com parâmetros padrão (aqueles que procure um Char na ocorrência de string) executar uma comparação ordinal, enquanto outros (aqueles que procurar uma seqüência na ocorrência de string) são cultura-sensível. É difícil lembrar de qual método usa o valor padrão e fácil confundir as sobrecargas.

  • A intenção do código que depende de valores padrão para chamadas de método não está clara. No exemplo a seguir, que se baseia nos padrões, é difícil saber se o desenvolvedor realmente pretendia um ordinal ou uma comparação lingüística de duas seqüências de caracteres, ou se a diferença entre maiúsculas protocol e "http" pode fazer com que o teste de igualdade retornar false.

    Dim protocol As String = GetProtocol(url)       
    If String.Equals(protocol, "http") Then
       ' ...Code to handle HTTP protocol.
    Else
       Throw New InvalidOperationException()
    End If   
    
    string protocol = GetProtocol(url);       
    if (String.Equals(protocol, "http")) {
       // ...Code to handle HTTP protocol.
    }
    else {
       throw new InvalidOperationException();
    }
    

Em geral, é recomendável que você chamar um método que não contam com os padrões, porque torna a intenção do código inequívoca. Isso, por sua vez, torna o código mais legível e fácil de depurar e manter. O exemplo a seguir é equivalente ao código anterior, exceto que, é claro que ordinal comparação é usada e que diferenças caso são ignorados.

Dim protocol As String = GetProtocol(url)       
If String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase) Then
   ' ...Code to handle HTTP protocol.
Else
   Throw New InvalidOperationException()
End If   
string protocol = GetProtocol(url);       
if (String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase)) {
   // ...Code to handle HTTP protocol.
}
else {
   throw new InvalidOperationException();
}

Voltar ao topo

Os detalhes da comparação de seqüência de caracteres

Comparação de seqüência de caracteres é o coração de muitos relacionados a seqüência de operações, particularmente, classificação e teste de igualdade. Classificação de cadeias de caracteres em uma determinada ordem: Se "Meu" aparece antes do "string" em uma lista classificada de seqüências de caracteres, "Meu" deve comparar menor ou igual a "string". Além disso, a comparação define implicitamente a igualdade. A operação de comparação retorna zero para seqüências de caracteres que julgar igual. Uma boa interpretação é que nenhuma seqüência de caracteres é menor do que o outro. Operações mais significativas, que envolvem seqüências incluem um ou ambos destes procedimentos: Comparar com outra seqüência e executar uma operação de classificação bem definida.

No entanto, avaliar as duas seqüências de caracteres para a ordem de classificação ou de igualdade não produz um único resultado correto; o resultado depende dos critérios usados para comparar as cadeias de caracteres. Em particular, comparações de seqüência de caracteres que são ordinal ou que se baseiam as maiúsculas e minúsculas e classificação de convenções de cultura atual ou a cultura invariável (uma cultura independente de localidade com base no idioma inglês) podem produzir resultados diferentes.

Comparações de seqüência de caracteres que usam a cultura atual

Um critério envolve usando as convenções da cultura atual, ao comparar seqüências de caracteres. Comparações baseiam-se a cultura atual usam cultura atual ou a localidade do thread. Se a cultura não é definida pelo usuário, o padrão para a configuração do Opções regionais janela no painel de controle. Você deve sempre usar comparações sejam baseiam na cultura atual quando dados lingüisticamente relevantes, e quando ela reflete a interação do usuário de cultura.

No entanto, de comparação e comportamento de maiúsculas e minúsculas na.NET Framework é alterado quando a cultura é alterado. Isso acontece quando um aplicativo é executado em um computador que tem uma cultura diferente do computador no qual o aplicativo foi desenvolvido, ou quando o thread de execução muda sua cultura. Esse comportamento é intencional, mas permanece não óbvio para muitos desenvolvedores. O exemplo a seguir ilustra as diferenças na ordem de classificação entre os Estados Unidos Inglês ("en-US") e culturas do Sueco ("sv-SE"). Observe que as palavras "ångström", "Windows" e "Visual Studio" aparecem em posições diferentes nas matrizes de seqüência classificada.

Imports System.Globalization
Imports System.Threading

Module Example
   Public Sub Main()
      Dim values() As String = { "able", "ångström", "apple", _
                                 "Æble", "Windows", "Visual Studio" }
      Array.Sort(values)
      DisplayArray(values)

      ' Change culture to Swedish (Sweden).
      Dim originalCulture As String = 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)
    End Sub

    Private Sub DisplayArray(values() As String)
      Console.WRiteLine("Sorting using the {0} culture:", _ 
                        CultureInfo.CurrentCulture.Name)
      For Each value As String In values
         Console.WriteLine("   {0}", value)
      Next
      Console.WriteLine()   
    End Sub
End Module
' 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
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

Comparações entre maiúsculas e minúsculas que usam a cultura atual são os mesmos comparações de cultura, exceto que eles ignoram maiúsculas e minúsculas, conforme indicado pela cultura atual do segmento. Esse comportamento pode se manifestar em ordens de classificação também.

Comparações de semântica de cultura atual são o padrão para os seguintes métodos:

Em qualquer caso, recomendamos que você chamar uma sobrecarga que tem um StringComparison parâmetro para tornar a intenção de limpar de chamada de método.

Bugs sutis e não tão sutis podem surgir quando os dados de seqüência lingüística são interpretados lingüisticamente ou dados de seqüência de caracteres de uma cultura específica são interpretados usando as convenções da cultura de outra. O exemplo canônico é o turco-problema do I.

Para quase todos os alfabetos latinos, incluindo o U.S. Inglês, o caractere "i" (\u0069) é a versão em letra minúscula do caractere "I" (\u0049). Esta regra de capitalização rapidamente se tornará o padrão para alguém como uma cultura de programação. No entanto, o alfabeto Turco ("tr-TR") inclui um "I com ponto" caractere "İ" (\u0130), que é a versão de capital do "i". Turco também inclui uma "i sem um ponto" de minúsculas caractere, "ı" (\u0131), que coloca em maiúscula "I". Esse comportamento ocorre na cultura Azeri ("az").

Portanto, as suposições feitas sobre o "i" de capitalização ou minúsculas "I" não são válidos entre todas as culturas. Se você usar sobrecargas de padrão para rotinas de comparação de seqüência de caracteres, eles estarão sujeitos a variação entre culturas. Se os dados a ser comparado não lingüísticos, usar as sobrecargas de padrão pode produzir resultados indesejáveis, como a seguinte tentativa de realizar uma comparação entre maiúsculas e minúsculas de seqüências de caracteres "arquivo" e "Arquivo" ilustra.

Imports System.Globalization
Imports System.Threading

Module Example
   Public Sub Main()
      Dim 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, Nothing))
      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, Nothing))
   End Sub
End Module
' The example displays the following output:
'       Culture = English (United States)
'       (file == FILE) = True
'       
'       Culture = Turkish (Turkey)
'       (file == FILE) = False
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

Essa comparação pode causar problemas significativos, se a cultura inadvertidamente é usada em configurações de segurança sensíveis, como no exemplo a seguir. Uma chamada de método, como IsFileURI("file:") retorna true se a cultura atual for U.S. Inglês, mas false se a cultura atual é turco. Assim, em sistemas turco, alguém poderia burlar as medidas de segurança que bloqueiam o acesso para URIs de maiúsculas e minúsculas que começam com "arquivo:".

Public Shared Function IsFileURI(path As String) As Boolean 
   Return path.StartsWith("FILE:", True, Nothing)
End Function
public static bool IsFileURI(String path) 
{
   return path.StartsWith("FILE:", true, null);
}

Nesse caso, porque "arquivo:" é deve ser interpretado como um identificador-lingüístico, diferenciação de cultura, o código deve em vez disso, ser escrito como mostrado no exemplo a seguir.

Public Shared Function IsFileURI(path As String) As Boolean 
    Return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)
End Function
public static bool IsFileURI(string path) 
{
   path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
   return true;
}

No entanto, o exemplo anterior usa o String.StartsWith(String, StringComparison) método para testar igualdade. Como a finalidade da comparação é testar igualdade em vez de ordenação de seqüências de caracteres, uma melhor alternativa é chamar o Equals método, conforme mostrado no exemplo a seguir.

Public Shared Function IsFileURI(path As String) As Boolean
   If (path.Length < 5) Then Return False

   Return String.Equals(path.Substring(0, 5), "FILE:", _ 
                        StringComparison.OrdinalIgnoreCase)
End Function   
public static bool IsFileURI(string path)
{
   if (path.Length < 5) return false;

   return String.Equals(path.Substring(0, 5), "FILE:", 
                        StringComparison.OrdinalIgnoreCase);
}   

Operações de cadeia de caracteres ordinal

Especificando o StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase o valor em uma chamada de método é uma comparação lingüística no qual os recursos de linguagens naturais são ignorados. Os métodos são invocados com essas StringComparison valores basear as decisões de operação da seqüência de caracteres em comparações de byte simples em vez de maiúsculas e minúsculas ou tabelas de equivalência, que são parametrizadas por cultura. Na maioria dos casos, essa abordagem melhor ajusta a interpretação pretendida de seqüências de caracteres, enquanto torna o código mais rápido e confiável.

Comparações ordinais são comparações de seqüência em que cada byte de cada seqüência de caracteres é comparado sem interpretação lingüística; Por exemplo, "windows" não corresponde a "Windows". Isso é essencialmente uma chamada para o c runtime strcmp função. Use essa comparação quando o contexto determina a seqüências de caracteres devem ser correspondidas exatamente ou demandas conservadora diretiva correspondente. Além disso, a comparação ordinal é a operação de comparação mais rápida porque ele se aplica sem regras lingüísticas ao determinar um resultado.

Strings na.NET Framework pode conter caracteres nulos inseridos. Uma das diferenças mais clara entre a comparação ordinal e cultura que (incluindo as comparações de cultura invariável) se refere a manipulação de caracteres de nulo incorporados em uma seqüência. Esses caracteres são ignorados quando você usa o String.Compare e String.Equals métodos para realizar comparações de cultura (incluindo as comparações de cultura invariável). Como resultado, nas comparações de cultura, seqüências que contêm caracteres nulos inseridos podem ser consideradas iguais para seqüências de caracteres que não.

Observação importanteImportante

Embora os métodos de comparação de seqüência de caracteres desconsiderar caracteres nulos inseridos, string métodos de pesquisa, como String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf, e String.StartsWith fazer não.

O exemplo a seguir executa uma comparação de cultura da seqüência de caracteres "Aa" com uma seqüência de caracteres semelhante que contém vários caracteres de nulos inseridos entre "A" e "a" e mostra como as duas seqüências de caracteres são consideradas iguais.

Module Example
   Public Sub Main()
      Dim str1 As String = "Aa"
      Dim str2 As String = "A" + New String(Convert.ToChar(0), 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))
   End Sub

   Private Function ShowBytes(str As String) As String
      Dim hexString As String = String.Empty
      For ctr As Integer = 0 To str.Length - 1
         Dim result As String = String.Empty
         result = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
         result = " " + result.Substring(0,2) + " " + result.Substring(2, 2)
         hexString += result
      Next
      Return hexString.Trim()
   End Function
End Module
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

No entanto, as seqüências de caracteres não são consideradas iguais ao usar a comparação ordinal, como mostra o exemplo a seguir.

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
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

Comparações ordinais de maiúsculas e minúsculas são a abordagem conservadora mais próxima. Essas comparações ignorar a maioria das maiúsculas; Por exemplo, "windows" corresponde a "Windows". Ao lidar com caracteres ASCII, esta diretiva é equivalente a StringComparison.Ordinal, exceto que ele ignora maiúsculas e minúsculas usual do ASCII. Portanto, qualquer caractere em [A Z] (\u0041-\u005A) coincide com o caractere correspondente em [a, z] (\u0061-\007A). A capitalização fora do intervalo ASCII usa tabelas da cultura invariável. Portanto, a comparação seguinte:

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

é o equivalente a (mas mais rápido do que) Esta comparação:

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

Essas comparações ainda são muito rápidas.

Observação

O comportamento de seqüência de caracteres do sistema de arquivos, chaves do registro e valores e variáveis de ambiente melhor é representado por StringComparison.OrdinalIgnoreCase.

Ambos StringComparison.Ordinal e StringComparison.OrdinalIgnoreCase usar os valores binários diretamente e são mais adequados para correspondência. Quando você não tiver certeza sobre as configurações de comparação, use um destes dois valores. No entanto, pois eles realizam uma comparação byte por byte, eles não classificar por uma ordem de classificação lingüísticas (como um dicionário de inglês), mas por ordem de classificação binária. Os resultados podem parecer estranhas na maioria dos contextos se exibido aos usuários.

Semântica ordinal é o padrão para String.Equals sobrecargas que não incluem um StringComparison argumento (incluindo o operador de igualdade). Em qualquer caso, recomendamos que você chamar uma sobrecarga que tem um StringComparison parâmetro.

Operações de cadeia de caracteres que usam a cultura invariável

Comparações com o uso de cultura invariável a CompareInfo propriedade retornado pelo estática CultureInfo.InvariantCulture propriedade. Esse comportamento é o mesmo em todos os sistemas; ele converte todos os caracteres fora do seu intervalo em que acredita são caracteres invariável equivalentes. Esta diretiva pode ser útil para manter a um conjunto de comportamento de seqüência de caracteres entre culturas, mas geralmente fornece resultados inesperados.

Comparações entre maiúsculas e minúsculas, com a cultura invariável usam estática CompareInfo propriedade retornado pelo estática CultureInfo.InvariantCulture propriedade para informações de comparação como bem. Quaisquer diferenças de maiúsculas entre esses caracteres traduzidos são ignoradas.

As comparações que usam StringComparison.InvariantCulture e StringComparison.Ordinal trabalhar idêntico em seqüências de caracteres em ASCII. No entanto, StringComparison.InvariantCulture toma decisões lingüísticas, que podem não ser apropriadas para seqüências de caracteres que precisam ser interpretado como um conjunto de bytes. O CultureInfo.InvariantCulture.CompareInfo objeto faz o Compare método interpretar determinados conjuntos de caracteres como equivalente. Por exemplo, a equivalência a seguir é válida em cultura invariável:

InvariantCulture: um + ̊ = å

O caractere de LETRA latina minúscula A "a" (\u0061), quando ele estiver próximo ao caractere COMBINANDO ANEL acima de "+"̊" (\u030a) é interpretado como o å"LATINO pequena LETRA A com ANEL acima caractere" (\u00e5). Como mostra o exemplo a seguir, esse comportamento difere de comparação ordinal.

Dim separated As String = ChrW(&h61) + ChrW(&h30a)
Dim combined As String = ChrW(&he5)

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
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      

Ao interpretar nomes de arquivo, cookies ou coisa onde uma combinação como "å" podem aparecer, comparações ordinais ainda oferecem o comportamento mais transparente e ajuste.

No saldo, a cultura invariável tem muito poucas propriedades que o tornam útil para comparação. Ele faz a comparação de maneira lingüisticamente relevante, o que impede que ele garantindo toda simbólica equivalência, mas não é a opção para exibição em qualquer cultura. Um dos motivos para usar algumas StringComparison.InvariantCulture para comparação é persistir ordenados de dados para uma exibição cross-culturally idênticos. Por exemplo, se um arquivo grande de dados que contém uma lista de identificadores de classificados para exibição acompanha um aplicativo, adicionando a essa lista exigiria uma inserção com classificação de estilo invariável.

Voltar ao topo

Escolhendo o membro StringComparison para a chamada de método

A tabela a seguir descreve o mapeamento do contexto de semântica de seqüência de caracteres para um StringComparison membro de enumeração.

Dados

Comportamento

Correspondente de System.StringComparison

Valor

Identificadores de internos diferencia maiúsculas de minúsculas.

Identificadores de maiúsculas e minúsculas em padrões como XML e HTTP.

Configurações relacionadas à segurança diferencia maiúsculas de minúsculas.

Um identificador lingüístico, onde os bytes coincidam exatamente.

Ordinal

Identificadores de internos de maiúsculas e minúsculas.

Identificadores de maiúsculas e minúsculas em padrões como XML e HTTP.

Caminhos de arquivo.

Chaves do registro e valores.

Variáveis de ambiente.

Identificadores de recurso (por exemplo, nomes de identificador).

Configurações relacionadas à segurança diferencia maiúsculas de minúsculas.

Um identificador lingüístico, onde o caso é irrelevante; especialmente dados armazenados na maioria dos serviços de sistema do Windows.

OrdinalIgnoreCase

Alguns dados persistentes, lingüisticamente relevantes.

Exibição de dados lingüísticos que requer uma ordem de classificação fixo.

Culturalmente agnósticos dados ainda lingüisticamente relevantes.

InvariantCulture

- ou -

InvariantCultureIgnoreCase

Dados exibidos ao usuário.

A maioria das entradas do usuário.

Dados que requer costumes lingüísticos locais.

CurrentCulture

- ou -

CurrentCultureIgnoreCase

Voltar ao topo

Métodos comuns de comparação de seqüência de caracteres na.NET Framework

As seções a seguir descrevem os métodos que são mais comumente usados para comparação de seqüência de caracteres.

String. Compare

Interpretação de padrão: StringComparison.CurrentCulture.

Como a operação mais central à interpretação de seqüência de caracteres, todas as instâncias dessas chamadas de método devem ser examinadas para determinar se cadeias de caracteres devem ser interpretadas de acordo com a cultura atual ou dissociar a cultura (simbolicamente). Normalmente, é o último e um StringComparison.Ordinal , comparação deve ser usada.

O System.Globalization.CompareInfo classe, que é retornado pelo CultureInfo.CompareInfo também inclui uma propriedade, um Compare método fornece um grande número de correspondência de opções (ordinal ignorando o espaço em branco, caracteres tipo kana ignorando e assim por diante) por meio do CompareOptions enumeração do sinalizador.

String.CompareTo

Interpretação de padrão: StringComparison.CurrentCulture.

Este método não oferece atualmente uma sobrecarga que especifica um StringComparison tipo. É possível converter esse métodos recomendado String.Compare(String, String, StringComparison) formulário.

Tipos que implementam o IComparable e IComparable<T> interfaces implementam este método. Porque ela não oferece a opção de um StringComparison parâmetro, a implementação de tipos geralmente permitem ao usuário especificar um StringComparer em seu construtor. O exemplo a seguir define uma FileName classe cujos construtor de classe inclui uma StringComparer parâmetro. Isso StringComparer objeto é usado na FileName.CompareTo método.

Public Class FileName : Implements IComparable
   Dim fname As String
   Dim comparer As StringComparer 

   Public Sub New(name As String, comparer As StringComparer)
      If String.IsNullOrEmpty(name) Then
         Throw New ArgumentNullException("name")
      End If

      Me.fname = name

      If comparer IsNot Nothing Then
         Me.comparer = comparer
      Else
         Me.comparer = StringComparer.OrdinalIgnoreCase
      End If      
   End Sub

   Public ReadOnly Property Name As String
      Get
         Return fname
      End Get   
   End Property

   Public Function CompareTo(obj As Object) As Integer _
          Implements IComparable.CompareTo
      If obj Is Nothing Then Return 1

      If Not TypeOf obj Is FileName Then
         obj = obj.ToString()
      Else
         obj = CType(obj, FileName).Name
      End If         
      Return comparer.Compare(Me.fname, obj)
   End Function
End Class
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);
   }
}

String. Equals

Interpretação de padrão: StringComparison.Ordinal.

O String classe permite testar igualdade chamando a estáticas ou instância de Equals sobrecargas do método, ou usando o operador de igualdade estática. O operador e sobrecargas usam a comparação ordinal por padrão. No entanto, ainda recomendamos que você chamar uma sobrecarga que especifica explicitamente o StringComparison tipo mesmo se você deseja realizar uma comparação ordinal; Isso facilita o código para uma determinada interpretação de seqüência de caracteres de pesquisa.

String. ToUpper e String. ToLower

Interpretação de padrão: StringComparison.CurrentCulture.

Tenha cuidado ao usar esses métodos, como forçar uma seqüência de caracteres para um maiúsculas ou minúsculas geralmente é usada como uma pequena normalização para comparar strings indiferentemente do caso. Nesse caso, considere o uso de uma comparação diferenciando maiúsculas de minúsculas.

O String.ToUpperInvariant e String.ToLowerInvariant métodos também estão disponíveis. ToUpperInvarianté a maneira padrão para normalizar o caso. Comparações feitas usando StringComparison.OrdinalIgnoreCase a composição de duas chamadas de maneira comportamental são: chamando ToUpperInvariant em argumentos de seqüência de caracteres e fazendo uma comparação usando StringComparison.Ordinal.

Sobrecargas também estão disponíveis para converter para maiúsculas e minúsculas em uma cultura específica, passando um CultureInfo objeto que representa a cultura ao método.

Char.ToUpper e Char.ToLower

Interpretação de padrão: StringComparison.CurrentCulture.

Esses métodos funcionam da mesma forma como o String.ToUpper e String.ToLower métodos descritos na seção anterior.

String. StartsWith e String.EndsWith

Interpretação de padrão: StringComparison.CurrentCulture.

Por padrão, esses dois métodos realiza uma comparação de cultura.

String. IndexOf e String.LastIndexOf

Interpretação de padrão: StringComparison.CurrentCulture.

Há uma falta de consistência no como sobrecargas de padrão desses métodos realizar comparações. Todos os String.IndexOf e String.LastIndexOf métodos que incluem um Char parâmetro realizar uma comparação ordinal, mas o padrão String.IndexOf e String.LastIndexOf métodos que incluem um String parâmetro realizar uma comparação de cultura.

Se você chamar o String.IndexOf(String) ou String.LastIndexOf(String) método e passá-lo em uma seqüência de caracteres para localizar a instância atual, recomendamos que você chamar uma sobrecarga que especifica explicitamente o StringComparison tipo. As sobrecargas que incluem um Char argumento não permitem que você especifique um StringComparison tipo.

Voltar ao topo

Métodos que executam a comparação de seqüência de caracteres indiretamente

Alguns métodos de seqüência de caracteres que não têm a comparação de seqüência de caracteres como uma central de operação, use o StringComparer tipo. O StringComparer classe inclui seis propriedades estáticas que retornam StringComparer instâncias cuja StringComparer.Compare métodos executam os seguintes tipos de comparações de seqüência de caracteres:

Array. Sort e Array.BinarySearch

Interpretação de padrão: StringComparison.CurrentCulture.

Quando você armazena todos os dados em uma coleção ou ler dados persistentes de um arquivo ou banco de dados em uma coleção, alternando a cultura atual pode invalidar as constantes na coleção. O Array.BinarySearch método pressupõe que os elementos na matriz a ser pesquisada já estão classificados. Para classificar qualquer elemento da seqüência de caracteres da matriz, o Array.Sort chamadas de método de String.Compare método para solicitar elementos individuais. Usar um comparador de cultura pode ser perigosa se as alterações de cultura entre a hora em que a matriz é classificada e seu conteúdo são pesquisadas. Por exemplo, no código a seguir, armazenamento e recuperação de operam no comparador fornecido implicitamente pela Thread.CurrentThread.CurrentCulture propriedade. Se a cultura pode alterar entre as chamadas para StoreNames e DoesNameExist, e especialmente se o conteúdo da matriz é mantido em algum lugar entre as chamadas de método de dois, a pesquisa de binária pode falhar.

' Incorrect.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)

   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next

   Array.Sort(names)          ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name) >= 0      ' Line B.
End Function
// 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.
}

Uma variação recomendada é exibido no exemplo a seguir, que usa o mesmo método de comparação ordinal de (diferenciação de cultura) para classificar e pesquisar o array. O código de alteração é refletido nas linhas rotuladas Line A e Line B em dois exemplos.

' Correct.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)

   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next

   Array.Sort(names, StringComparer.Ordinal)           ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name, StringComparer.Ordinal) >= 0      ' Line B.
End Function
// 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.
}

Se esses dados são persistentes e movidos entre culturas, e a classificação é usada para apresentar esses dados para o usuário, você pode considerar o uso StringComparison.InvariantCulture, que opera lingüisticamente para saída de usuário melhor, mas não é afetada por alterações na cultura. O exemplo a seguir modifica os dois exemplos anteriores para usar a cultura invariável para classificação e pesquisa da matriz.

' Correct.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)

   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next

   Array.Sort(names, StringComparer.InvariantCulture)           ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name, StringComparer.InvariantCulture) >= 0      ' Line B.
End Function
// 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.
}

Exemplo de coleções: Hashtable construtor

Seqüências de hash fornece um segundo exemplo de uma operação é afetado pela maneira na qual as cadeias são comparadas.

O exemplo a seguir instancia um Hashtable objeto passando-o StringComparer o objeto retornado pelo StringComparer.OrdinalIgnoreCase propriedade. Porque uma classe StringComparer que é derivada de StringComparer implementa o IEqualityComparer interface, sua GetHashCode método é usado para calcular o código hash de seqüências de caracteres na tabela de hash.

Const initialTableCapacity As Integer = 100
Dim h As Hashtable

Public Sub PopulateFileTable(dir As String)
   h = New Hashtable(initialTableCapacity, _
                     StringComparer.OrdinalIgnoreCase)

   For Each filename As String In Directory.GetFiles(dir)
      h.Add(filename, File.GetCreationTime(filename))
   Next                        
End Sub

Public Sub PrintCreationTime(targetFile As String)
   Dim dt As Object = h(targetFile)
   If dt IsNot Nothing Then
      Console.WriteLine("File {0} was created at {1}.", _
         targetFile, _
         CDate(dt))
   Else
      Console.WriteLine("File {0} does not exist.", targetFile)
   End If
End Sub  
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);
   }
}

Voltar ao topo