Partilhar via


Como comparar strings em C#

Comparas cadeias para responder a uma de duas perguntas: "Estas duas cadeias são iguais?" ou "Em que ordem devem estas cadeias ser colocadas ao organizá-las?"

Os seguintes fatores complicam estas duas questões:

  • Pode escolher uma comparação ordinal ou linguística.
  • Pode escolher se o caso for relevante.
  • Podes escolher comparações específicas por cultura.
  • As comparações linguísticas dependem da cultura e da plataforma.

Os System.StringComparison campos de enumeração representam estas escolhas:

  • CurrentCulture: Compare cadeias usando regras de ordenação sensíveis à cultura e à cultura atual.
  • CurrentCultureIgnoreCase: Compare cadeias usando regras de ordenação sensíveis à cultura, a cultura atual e ignorando o caso das cadeias que estão a ser comparadas.
  • InvariantCulture: Compare cadeias usando regras de ordenação sensíveis à cultura e a cultura invariante.
  • InvariantCultureIgnoreCase: Compare cadeias usando regras de ordenação sensíveis à cultura, a cultura invariante, e ignorando o caso das cadeias que estão a ser comparadas.
  • Ordinal: Comparar strings utilizando as regras de ordenação binária (ordinal).
  • OrdinalIgnoreCase: Compare strings usando regras de ordenação ordinal (binária) e ignorando o caso das strings que estão a ser comparadas.

Quando comparas cordas, defines uma ordem entre elas. As comparações são usadas para ordenar uma sequência de cadeias. Uma vez que a sequência está numa ordem conhecida, torna-se mais fácil pesquisar, tanto por software como por humanos. Outras comparações podem verificar se as cordas são iguais. Estas verificações de igualdade são semelhantes à igualdade, mas algumas diferenças, como as diferenças de casos, podem ser ignoradas.

Comparações ordinais padrão

Por padrão, as operações mais comuns:

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

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

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

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

A comparação ordinal padrão não tem em conta as regras linguísticas ao comparar cadeias. Compara o valor binário de cada Char objeto em duas cadeias. Consequentemente, a comparação ordinal padrão também distingue entre maiúsculas e minúsculas.

O teste de igualdade com String.Equals e os operadores == e != difere da comparação de strings usando os métodos String.CompareTo e Compare(String, String). Todos realizam uma comparação sensível a maiúsculas e minúsculas. No entanto, enquanto os testes de igualdade fazem uma comparação ordinal, os métodos CompareTo e Compare realizam uma comparação linguística sensível à cultura usando a cultura atual. Torne clara a intenção do seu código ao chamar uma sobrecarga que especifique explicitamente o tipo de comparação a ser realizada.

Pode usar o is operador e um padrão constante como alternativa a == quando o operando direito é constante.

Comparações ordinais insensíveis a maiúsculas e minúsculas

O método String.Equals(String, StringComparison) permite-lhe especificar um valor StringComparison de StringComparison.OrdinalIgnoreCase para uma comparação ordinal sem distinção de maiúsculas e minúsculas. Existe também um método estático String.Compare(String, String, StringComparison) que realiza uma comparação ordinal insensível ao caso se especificar um valor de StringComparison.OrdinalIgnoreCase para o StringComparison argumento. Estas comparações são mostradas no seguinte código:

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

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

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

Estes métodos utilizam as convenções de revestimento da cultura invariante ao realizar uma comparação ordinal insensível ao caso.

Comparações linguísticas

Muitos métodos de comparação de sequências (como String.StartsWith) usam regras linguísticas para a cultura atual por padrão para ordenar as entradas. Esta comparação linguística é por vezes referida como "ordem de ordenação das palavras". Quando se realiza uma comparação linguística, alguns caracteres Unicode não alfanuméricos podem ter pesos especiais atribuídos. Por exemplo, o hífen "-" pode ter um pequeno peso atribuído de modo que "co-op" e "coop" apareçam lado a lado em ordem de classificação. Determinados caracteres de controlo não imprimíveis podem ser ignorados. Além disso, alguns caracteres Unicode podem ser equivalentes a uma sequência de Char instâncias. O exemplo seguinte utiliza a expressão "Eles dançam na rua." em alemão, com o "ss" (U+0073 U+0073) numa corda e o 'ß' (U+00DF) noutra. Linguisticamente (no Windows), "ss" é igual ao carácter alemão Esszet: 'ß' tanto nas culturas "en-US" como "de-DE".

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

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

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

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

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

    if (compareOrdinal < 0)
    {
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    }
    else if (compareOrdinal > 0)
    {
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    }
    else
    {
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
    }
}

No Windows, antes do .NET 5, a ordem de ordenação de "cop", "coop" e "co-op" muda quando se passa de uma comparação linguística para uma comparação ordinal. As duas frases alemãs também se comparam de forma diferente usando os diferentes tipos de comparação. Antes do .NET 5, as APIs de globalização do .NET utilizavam bibliotecas de Suporte Nacional de Línguas (NLS ). No .NET 5 e versões posteriores, as APIs de globalização do .NET utilizam bibliotecas de Componentes Internacionais para Unicode (ICU), que unificam o comportamento de globalização do .NET em todos os sistemas operativos suportados.

Comparações usando culturas específicas

O exemplo seguinte armazena objetos CultureInfo para as culturas en-US e de-DE. As comparações são realizadas usando um CultureInfo objeto para garantir uma comparação específica por cultura. A cultura utilizada afeta as comparações linguísticas. O exemplo seguinte mostra os resultados da comparação das duas frases alemãs usando a cultura "en-US" e a cultura "de-DE":

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

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

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

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

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

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

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

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

    if (compareOrdinal < 0)
    {
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    }
    else if (compareOrdinal > 0)
    {
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    }
    else
    {
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
    }
}

Comparações sensíveis à cultura são tipicamente usadas para comparar e ordenar cadeias introduzidas pelos utilizadores com outras cadeias introduzidas pelos utilizadores. Os caracteres e as convenções de ordenação destas cadeias podem variar consoante a localização do computador do utilizador. Mesmo as cadeias que contêm caracteres idênticos podem ser ordenadas de forma diferente dependendo da cultura do tópico atual.

Ordenamento linguístico e pesquisa de cadeias de caracteres em arrays

Os exemplos seguintes mostram como ordenar e pesquisar cadeias de caracteres num array usando uma comparação linguística dependente da cultura atual. Usas os métodos estáticos Array que recebem um parâmetro System.StringComparer.

O exemplo seguinte mostra como ordenar um array de cadeias usando a cultura atual:

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

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

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

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

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

Depois de o array estar ordenado, pode procurar entradas usando uma pesquisa binária. Uma pesquisa binária começa a meio da coleção para determinar qual metade da coleção conteria a cadeia procurada. Cada comparação subsequente subdivide a parte restante da coleção em metade. O array é ordenado usando o StringComparer.CurrentCulture. A função ShowWhere local apresenta informações sobre onde a cadeia foi encontrada. Se a cadeia não foi encontrada, o valor devolvido indica onde ela estaria se fosse encontrada.

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

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

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

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

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

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

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

Ordenação ordinal e pesquisa em coleções

O código seguinte utiliza a System.Collections.Generic.List<T> classe collection para armazenar strings. As cadeias são ordenadas usando este List<T>.Sort método. Este método necessita de um delegado que compare e ordene duas cadeias. O String.CompareTo método fornece essa função de comparação. Executa a amostra e observa a ordem. Esta operação de ordenação utiliza uma ordenação ordinal sensível a maiúsculas e minúsculas. Usarias os métodos estáticos String.Compare para especificar diferentes regras de comparação.

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

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

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

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

Uma vez ordenada, a lista de cadeias pode ser pesquisada usando uma pesquisa binária. O exemplo seguinte mostra como pesquisar a lista ordenada usando a mesma função de comparação. A função ShowWhere local mostra onde está ou seria o texto procurado:

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

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

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

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

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

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

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

Certifique-se sempre de usar o mesmo tipo de comparação para ordenar e pesquisar. Usar diferentes tipos de comparação para ordenação e pesquisa produz resultados inesperados.

Classes de coleção como System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue> e System.Collections.Generic.List<T> têm construtores que aceitam um parâmetro System.StringComparer quando o tipo dos elementos ou chaves é string. Em geral, deve utilizar estes construtores sempre que possível e especificar StringComparer.Ordinal ou StringComparer.OrdinalIgnoreCase.

Consulte também