Como fazer a comparação de strinhs

Faça um programa para comparar se duas strings são iguais.

Comentários: este é mais um exercício para exercitarmos laços de repetição. Esta série referem-se as strings pois, sabemos que em C as strings se assemelham a um vetor (char string[]).

Linguagem C

Na linguagem C, como dito, sabemos que as strings em questão são 2 vetores e que o caminho mais provável seja comparar os valores de ambos os vetores um a um. Utilize o código abaixo para encontrar a sua solução.

#include <stdio.h> #include <assert.h> // // Função que compara se duas string são iguais // int compararStrings(char str1[], char str2[]) { // Aqui entra a lógica do programa. } // // Testes // int main() { char ola[] = "ola"; char ola2[] = "ola"; assert(1 == compararStrings(ola, ola2)); return 0; }

Solução na linguagem C

// // Função que compara se duas string são iguais // int compararStrings(char str1[], char str2[]) { int i = 0; do { if(str1[i] != str2[i]) { return 2; } i++; } while(str1[i] != 0); return 1; }

Desafio na linguagem C

A função abaixo também funciona, porém a implementação dela é bem diferente.

O desafio é entender o que está acontecendo.

int compararStrings(char str1[], char str2[]) { int i = 0; for (i = 0; str1[i] == str2[i]; i++) { if (str1[i] == 0) { return 1; } } return 2; }

Linguagem Python

Em Python, no terminal, vemos que nosso objetivo (compara duas strings) é facilmente alcançado.

>>> "palavra" == "palavra" True

Como seria a solução se tivéssemos que construir um laço de repetição?

Vamos praticar…

# -*- coding: utf-8 -*- # # Seu código # def compararString(a, b): pass # # Seu teste # cor1 = "laranja" cor2 = "amarelo" assert not compararString(cor1, cor2) assert compararString(cor1, cor1)

Solução na linguagem Python

# -*- coding: utf-8 -*- # # Seu código # def compararString(a, b): i = 0 while i < len(a): if a[i] != b[i]: return False break i += 1 return True # # Seu teste # cor1 = "laranja" cor2 = "amarelo" assert not compararString(cor1, cor2) assert compararString(cor1, cor1)

Linguagem JavaScript

Em JS temos a mesma facilidade….

> "laranja" == "laranja" true > "laranja" != "amarelo" true >

O laço de repetição é trivial.

Você compara cadeias de caracteres para responder a uma das duas perguntas: "Essas duas cadeias de caracteres são iguais?" ou "Em que ordem essas cadeias de caracteres devem ser colocadas ao classificá-las?"

Essas duas perguntas são complicadas devido a fatores que afetam as comparações de cadeia de caracteres:

  • Você pode escolher uma comparação ordinal ou linguística.
  • Você pode escolher se o uso de maiúsculas faz diferença.
  • Você pode escolher comparações específicas de cultura.
  • As comparações linguísticas dependem da plataforma e da cultura.

Observação

Os exemplos de C# neste artigo são executados no executador de código embutido Try.NET e no playground. Clique no botão Executar para executar um exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo e executar o código modificado clicando em Executar novamente. O código modificado será executado na janela interativa ou, se a compilação falhar, a janela interativa exibirá todos as mensagens de erro do compilador C#.

Ao comparar cadeias de caracteres, você pode definir uma ordem entre elas. As comparações são usadas para classificar uma sequência de cadeias de caracteres. Com a sequência em uma ordem conhecida, fica mais fácil de pesquisar, tanto para softwares quanto para os usuários. Outras comparações podem verificar se as cadeias de caracteres são iguais. Essas verificações são semelhantes a igualdade, mas algumas diferenças, como diferenças entre maiúsculas e minúsculas, podem ser ignoradas.

Comparações ordinárias padrão

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

executar uma comparação ordinal com diferenciação de maiúsculas e minúsculas. No caso de String.Equals, um StringComparison argumento pode ser fornecido para alterar suas regras de classificação. O exemplo a seguir demonstra que:

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 leva em conta as regras linguísticas ao comparar cadeias de caracteres. Ela compara o valor binário de cada objeto Char em duas cadeias de caracteres. Como resultado, a comparação ordinal padrão também diferencia maiúsculas de minúsculas.

O teste de igualdade com String.Equals e os == operadores e != os operadores diferem da comparação de cadeias de caracteres usando os métodos e Compare(String, String) os String.CompareTo métodos. Todos eles executam uma comparação que diferencia maiúsculas de minúsculas. No entanto, enquanto os testes de igualdade executam uma comparação ordinal, os métodos e Compare os CompareTo métodos executam uma comparação linguística com reconhecimento de cultura usando a cultura atual. Como esses métodos de comparação padrão diferem na maneira como eles comparam cadeias de caracteres, recomendamos que você sempre deixe clara a intenção do código chamando uma sobrecarga que especifica explicitamente o tipo de comparação a ser executada.

Comparações ordinais que não diferenciam maiúsculas de minúsculas

O String.Equals(String, StringComparison) método permite que você especifique um StringComparison valor para StringComparison.OrdinalIgnoreCase uma comparação ordinal que não diferencia maiúsculas de minúsculas. Há também um método String.Compare(String, String, StringComparison) estático que fará uma comparação ordinal sem distinção entre maiúsculas e minúsculas se você especificar um valor de StringComparison.OrdinalIgnoreCase para o argumento StringComparison. Eles são mostrados no código a seguir:

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");

Ao fazer uma comparação ordinal sem distinção entre maiúsculas e minúsculas, esses métodos usam as convenções de maiúsculas e minúsculas da cultura invariável.

Comparações linguísticas

As cadeias de caracteres também podem ser ordenadas usando regras linguísticas para a cultura atual. Às vezes, isso é chamado de "ordem de classificação de palavras". Quando você executa uma comparação linguística, alguns caracteres Unicode não numéricos podem ter pesos especiais atribuídos. Por exemplo, o hífen "-" pode ter um peso pequeno atribuído a ele para que "cooperativa" e "coop" apareçam um ao lado do outro na ordem de classificação. Além disso, alguns caracteres Unicode podem ser equivalentes a uma sequência de instâncias Char. O exemplo a seguir usa a frase "Eles dançam na rua" em alemão com os "ss" (U+0073 U+0073) em uma cadeia de caracteres e 'ß' (U+00DF) em outra. Linguisticamente (no Windows), "ss" é igual ao caractere 'ß' Esszet alemão nas culturas "en-US" e "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"); }

Em Windows, antes do .NET 5, a ordem de classificação de "cop", "coop" e "cooperativa" muda quando você muda de uma comparação linguística para uma comparação ordinal. As duas frases em alemão também são comparadas de forma diferente ao usar os tipos diferentes de comparação. Isso ocorre porque, antes do .NET 5, as APIs de globalização do .NET usavam bibliotecas do NLS (National Language Support ). No .NET 5 e versões posteriores, as APIs de globalização do .NET usam bibliotecas de Componentes Internacionais para Unicode (UTI), que unificam . O comportamento de globalização da NET em todos os sistemas operacionais com suporte.

Comparações usando culturas específicas

Este exemplo armazena objetos CultureInfo para as culturas en-US e de-DE. As comparações são feitas usando um objeto CultureInfo para garantir uma comparação específica da cultura.

A cultura usada afeta as comparações linguísticas. O exemplo a seguir mostra os resultados da comparação das duas frases em alemão 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"); }

As comparações que diferenciam cultura normalmente são usadas para comparar e classificar cadeias de caracteres inseridas por usuários com outras cadeias de caracteres inseridas por usuários. Os caracteres e as convenções de classificação dessas cadeias de caracteres podem variar de acordo com a localidade do computador do usuário. Até mesmo cadeias de caracteres que contêm caracteres idênticos podem ser classificadas de formas diferentes dependendo da cultura do thread atual.

Classificação linguística e cadeias de caracteres de pesquisa em matrizes

Os exemplos a seguir mostram como classificar e pesquisar cadeias de caracteres em uma matriz usando uma comparação linguística que depende da cultura atual. Use os métodos Array estáticos que aceitam um parâmetro System.StringComparer.

Este exemplo mostra como classificar uma matriz de cadeias de caracteres usando a cultura atual:

string[] lines = new string[] { @"c:\public\textfile.txt", @"c:\public\textFile.TXT", @"c:\public\Text.txt", @"c:\public\testfile2.txt" }; Console.WriteLine("Non-sorted order:"); foreach (string s in lines) { Console.WriteLine($" {s}"); } Console.WriteLine("\n\rSorted order:"); // Specify Ordinal to demonstrate the different behavior. Array.Sort(lines, StringComparer.CurrentCulture); foreach (string s in lines) { Console.WriteLine($" {s}"); }

Depois que a matriz é classificada, você pode procurar as entradas usando uma pesquisa binária. Uma pesquisa binária é iniciada no meio da coleção para determinar qual metade da coleção contém a cadeia de caracteres procurada. Cada comparação subsequente subdivide a parte restante da coleção na metade. A matriz é classificada usando o StringComparer.CurrentCulture. A função local ShowWhere exibe informações sobre o local em que a cadeia de caracteres foi encontrada. Se a cadeia de caracteres não foi encontrada, o valor retornado indica onde ela estaria se fosse encontrada.

string[] lines = new string[] { @"c:\public\textfile.txt", @"c:\public\textFile.TXT", @"c:\public\Text.txt", @"c:\public\testfile2.txt" }; Array.Sort(lines, StringComparer.CurrentCulture); string searchString = @"c:\public\TEXTFILE.TXT"; Console.WriteLine($"Binary search for <{searchString}>"); int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture); ShowWhere<string>(lines, result); Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); void ShowWhere<T>(T[] array, int index) { if (index < 0) { index = ~index; Console.Write("Not found. Sorts between: "); if (index == 0) Console.Write("beginning of sequence and "); else Console.Write($"{array[index - 1]} and "); if (index == array.Length) Console.WriteLine("end of sequence."); else Console.WriteLine($"{array[index]}."); } else { Console.WriteLine($"Found at index {index}."); } }

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

O código a seguir usa a classe de coleção System.Collections.Generic.List<T> para armazenar cadeias de caracteres. As cadeias de caracteres são classificadas usando o método List<T>.Sort. Esse método precisa de um delegado que compara e ordena as duas cadeias de caracteres. O método String.CompareTo fornece essa função de comparação. Execute o exemplo e observe a ordem. Essa operação de classificação usa uma classificação ordinal que diferencia maiúsculas de minúsculas. Você usaria os métodos String.Compare estáticos para especificar regras de comparação diferentes.

List<string> lines = new List<string> { @"c:\public\textfile.txt", @"c:\public\textFile.TXT", @"c:\public\Text.txt", @"c:\public\testfile2.txt" }; Console.WriteLine("Non-sorted order:"); foreach (string s in lines) { Console.WriteLine($" {s}"); } Console.WriteLine("\n\rSorted order:"); lines.Sort((left, right) => left.CompareTo(right)); foreach (string s in lines) { Console.WriteLine($" {s}"); }

Uma vez classificada, a lista de cadeias de caracteres pode ser pesquisada usando uma pesquisa binária. O exemplo a seguir mostra como pesquisar a lista classificada usando a mesma função de comparação. A função local ShowWhere mostra o local em que o texto procurado está ou deveria estar:

List<string> lines = new List<string> { @"c:\public\textfile.txt", @"c:\public\textFile.TXT", @"c:\public\Text.txt", @"c:\public\testfile2.txt" }; lines.Sort((left, right) => left.CompareTo(right)); string searchString = @"c:\public\TEXTFILE.TXT"; Console.WriteLine($"Binary search for <{searchString}>"); int result = lines.BinarySearch(searchString); ShowWhere<string>(lines, result); Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); void ShowWhere<T>(IList<T> collection, int index) { if (index < 0) { index = ~index; Console.Write("Not found. Sorts between: "); if (index == 0) Console.Write("beginning of sequence and "); else Console.Write($"{collection[index - 1]} and "); if (index == collection.Count) Console.WriteLine("end of sequence."); else Console.WriteLine($"{collection[index]}."); } else { Console.WriteLine($"Found at index {index}."); } }

Use sempre o mesmo tipo de comparação para classificação e pesquisa. O uso de tipos diferentes de comparação para classificaçã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 usam um parâmetro System.StringComparer quando o tipo dos elementos ou chaves é string. Em geral, você deve usar esses construtores sempre que possível e especificar StringComparer.Ordinal ou StringComparer.OrdinalIgnoreCase.

Igualdade de referência e centralização de cadeia de caracteres

Nenhum dos exemplos usou ReferenceEquals. Esse método determina se duas cadeias de caracteres são o mesmo objeto, o que pode levar a resultados inconsistentes em comparações de cadeia de caracteres. O exemplo a seguir demonstra o recurso de centralização da cadeia de caracteres do C#. Quando um programa declara duas ou mais variáveis de cadeia de caracteres idênticas, o compilador armazena todas no mesmo local. Chamando o método ReferenceEquals, você pode ver que as duas cadeias de caracteres na verdade se referem ao mesmo objeto na memória. Use o método String.Copy para evitar a centralização. Depois que a cópia for feita, as duas cadeias de caracteres terão locais de armazenamento diferentes, mesmo que tenham o mesmo valor. Execute o exemplo a seguir para mostrar que as cadeias de caracteres a e b são centralizadas, ou seja, que elas compartilham o mesmo armazenamento. As cadeias de caracteres a e c não são.

string a = "The computer ate my source code."; string b = "The computer ate my source code."; if (String.ReferenceEquals(a, b)) Console.WriteLine("a and b are interned."); else Console.WriteLine("a and b are not interned."); string c = String.Copy(a); if (String.ReferenceEquals(a, c)) Console.WriteLine("a and c are interned."); else Console.WriteLine("a and c are not interned.");

Observação

Quando você testa cadeias de caracteres quanto a igualdade, é necessário usar os métodos que especificam explicitamente o tipo de comparação que você pretende executar. O código fica muito mais legível e fácil de manter. Use as sobrecargas dos métodos das classes System.String e System.Array que aceitam um parâmetro de enumeração StringComparison. Você especifica o tipo de comparação a ser executado. Evite usar os operadores == e != ao testar a igualdade. Os métodos de instância String.CompareTo sempre executam uma comparação ordinal que diferencia maiúsculas de minúsculas. Basicamente, eles são adequados para colocar cadeias de caracteres em ordem alfabética.

Você pode internalizar uma cadeia de caracteres ou recuperar uma referência a uma cadeia de caracteres interna existente chamando o método String.Intern. Para determinar se uma cadeia de caracteres está internalizada, chame o método String.IsInterned.

Confira também