Pergunta

Se eu executar a seguinte declaração:

string.Compare("mun", "mün", true, CultureInfo.InvariantCulture)

O resultado é '-1', indicando que 'Mun' tem um valor numérico mais baixo que 'Mün'.

No entanto, se eu executar esta declaração:

string.Compare("Muntelier, Schweiz", "München, Deutschland", true, CultureInfo.InvariantCulture)

Eu recebo '1', indicando que 'Muntelier, Schewiz' deve ir por último.

Isso é um bug na comparação? Ou, mais provável, existe uma regra que eu deveria estar levando em consideração ao classificar seqüências contendo acentuadas


A razão pela qual isso é um problema é que estou classificando uma lista e, em seguida, fazendo um filtro binário manual destinado a obter todas as strings começando com 'xxx'.

Anteriormente, eu estava usando o método LINQ 'Where', mas agora tenho que usar essa função personalizada escrita por outra pessoa, porque ele diz que tem um desempenho melhor.

Mas a função personalizada não parece levar em consideração as regras 'unicode' .NET. Portanto, se eu disser para filtrar por 'Mün', ele não encontra itens, mesmo que haja itens na lista começando com 'MUN'.

Isso parece ser por causa da ordem inconsistente de caracteres acentuados, dependendo de quais personagens vão atrás do personagem acentuado.


Ok, acho que resolvi o problema.

Antes do filtro, faço uma classificação com base no primeiro n letras de cada string, onde n é o comprimento da sequência de pesquisa.

Foi útil?

Solução

Há um algoritmo de empate no trabalho, veja http://unicode.org/reports/tr10/

Para abordar as complexidades da classificação sensível à linguagem, é empregado um algoritmo de comparação multinível. Ao comparar duas palavras, por exemplo, a característica mais importante é o caractere de base: como a diferença entre A e A e A B. Decenciais são normalmente ignoradas, se houver alguma diferença nas letras básicas. As diferenças de casos (maiúsculas versus minúsculas) são normalmente ignoradas, se houver alguma diferença na base ou sotaques. A pontuação é variável. Em algumas situações, um caráter de pontuação é tratado como um caractere de base. Em outras situações, deve ser ignorado se houver alguma diferença de base, sotaque ou caso. Também pode haver um nível final de quebra de ligação, pelo qual, se não houver outras diferenças na sequência, a ordem do ponto (normalizada) é usada.

Então, "Munt ..." e "Münc ..." são alfabeticamente diferentes e classificados com base nos "T" e "C".

Considerando que, "Mun" e "Mün" são alfabeticamente os mesmos ("U" equivocado a "ü" em idiomas perdidos), de modo que os códigos de caracteres são comparados

Outras dicas

Parece que o personagem acentuado está sendo usado apenas em uma espécie de situação "tie -break" - em outras palavras, se as cordas forem iguais.

Aqui está algum código de exemplo para demonstrar:

using System;
using System.Globalization;

class Test
{
    static void Main()
    {
        Compare("mun", "mün");
        Compare("muna", "münb");
        Compare("munb", "müna");
    }

    static void Compare(string x, string y)
    {
        int result = string.Compare(x, y, true, 
                                   CultureInfo.InvariantCulture));

        Console.WriteLine("{0}; {1}; {2}", x, y, result);
    }
}

(Tentei adicionar um espaço depois do "n" também, para ver se foi feito nos limites das palavras - não é.)

Resultados:

mun; mün; -1
muna; münb; -1
munb; müna; 1

Suspeito que isso esteja correto por várias regras complicadas do Unicode - mas não sei o suficiente sobre elas.

Quanto a você precisar levar isso em consideração ... eu não esperaria assim. O que você está fazendo que é jogado por isso?

Pelo que entendi, ainda é um pouco consistente. Ao comparar usando CultureInfo.InvariantCulture o personagem Umlaut ü é tratado como o caráter não acentuado u.

Como as strings em seu primeiro exemplo obviamente não são iguais, o resultado não será 0, mas -1 (o que parece ser um valor padrão). No segundo exemplo Muntelier vai por último porque t segue c no alfabeto.

Não consegui encontrar nenhuma documentação clara no MSDN explicando essas regras, mas descobri que

string.Compare("mun", "mün", CultureInfo.InvariantCulture,  
    CompareOptions.StringSort);

e

string.Compare("Muntelier, Schweiz", "München, Deutschland", 
    CultureInfo.InvariantCulture, CompareOptions.StringSort);

dá o resultado desejado.

De qualquer forma, acho que você seria melhor basear sua classificação em uma cultura específica, como a cultura do usuário atual (se possível).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top