Manipulando a classificação em seus aplicativos

Alguns aplicativos, como Microsoft Active Directory, Microsoft Exchange e Microsoft Access, mantêm um banco de dados classificável de cadeias de caracteres de localidade e idioma indexadas por nome (cadeia de caracteres UTF-16) e seus pesos de classificação associados.

A classificação geralmente é intuitiva para os usuários em suas próprias localidades. No entanto, ele pode ser não intuitivo para desenvolvedores de aplicativos. Este tópico discute as considerações para lidar com a classificação em seus aplicativos. A classificação pode ser linguística ou ordinal (não linguística).

Funções de classificação

Você pode usar uma variedade de funções de classificação em seus aplicativos:

Normalmente, as funções de classificação avaliam cadeias de caracteres caractere por caractere. No entanto, muitas linguagens têm elementos de vários caracteres, como o par de dois caracteres "CH" em espanhol tradicional. CompareString e CompareStringEx usam o identificador de localidade fornecido pelo aplicativo ou o nome para identificar elementos de vários caracteres. Por outro lado, lstrcmp e lstrcmpi usam a localidade do usuário.

Outro exemplo é vietnamita, que contém muitos elementos de dois caracteres, como maiúsculas válidas, maiúsculas e minúsculas de "GI", que são "GI, "Gi" e "gi", respectivamente. Qualquer uma dessas formas é tratada como um único elemento de classificação e, se o uso de maiúsculas e minúsculas for ignorado, será comparado como igual. No entanto, como "gI" não é válido como um único elemento, CompareString, CompareStringEx, lstrcmp e lstrcmpi tratam "gI" como dois elementos separados.

As funções CompareString, CompareStringEx, lstrcmp, lstrcmpi, LCMapString, LCMapStringEx, FindNLSString e FindNLSStringEx são padrão para usar uma técnica de "classificação de palavras". Para esse tipo de tipo, todas as marcas de pontuação e outros caracteres não numéricos, exceto o hífen e o apóstrofo, vêm antes de qualquer caractere alfanumérico. O hífen e o apóstrofo são tratados de forma diferente dos outros caracteres não numéricos para garantir que palavras como "coop" e "cooperativa" permaneçam juntas em uma lista classificada.

Em vez de uma classificação de palavra, o aplicativo pode solicitar uma técnica de "classificação de cadeia de caracteres" das funções de classificação especificando o sinalizador SORT_STRINGSORT. Uma classificação de cadeia de caracteres trata o hífen e o apóstrofo como qualquer outro caractere não numérico. Suas posições na sequência de classificação estão antes dos caracteres alfanuméricos.

A tabela a seguir compara os resultados de uma classificação de palavra com os resultados de uma classificação de cadeia de caracteres.

Classificação Word Classificação de cadeia de caracteres
Boleto bill's
Contas Boleto
bill's Contas
não pode Não
Chanfro não pode
Não Chanfro
Con cooperação
Coop Con
cooperação Coop

 

Classificar cadeias de caracteres linguisticamente

As funções CompareString e CompareStringEx testam a igualdade linguística. Seus aplicativos devem usar essas funções com a localidade correta para classificar cadeias de caracteres linguisticamente.

Observação

Para compatibilidade com Unicode, um aplicativo deve preferir CompareStringEx ou a versão Unicode do CompareString. Outro motivo para preferir CompareStringEx é que a Microsoft está migrando para o uso de nomes de localidade em vez de identificadores de localidade para novas localidades, por motivos de interoperabilidade. Qualquer aplicativo executado somente no Windows Vista e posterior deve usar CompareStringEx.

 

Outra maneira de testar a igualdade linguística é usar lstrcmp ou lstrcmpi, que sempre usam uma classificação de palavra. A função lstrcmpi chama CompareString com o sinalizador NORM_IGNORECASE, enquanto lstrcmp a chama sem esse sinalizador. Para obter uma visão geral do uso das funções wrapper, consulte Cadeias de caracteres.

As funções recuperam resultados linguisticamente apropriados para todas as localidades. As expectativas do usuário para diferentes localidades podem diferir significativamente no comportamento de classificação, conforme mostrado nos exemplos a seguir.

  • Muitas localidades comparam a ligatura ae (æ) com as letras ae. No entanto, a Islândia (Islândia) considera-a uma carta separada e a coloca após Z na sequência de classificação.
  • O Anel A (Å) normalmente classifica com apenas uma diferença diacrítica de A. No entanto, sueco (Suécia) coloca o Anel A após Z na sequência de classificação.

As funções tentam verificar rigorosamente se os pontos de código definidos no padrão Unicode são canonicamente iguais a uma cadeia de caracteres de pontos de código equivalentes. Por exemplo, o ponto de código que representa um "u" minúsculo com uma dieresis (ü) é canonicamente igual a um "u" minúsculo combinado com a dieresis ( ̈). Observe, no entanto, que a equivalência canônica nem sempre é possível.

Como quase todos os dados inseridos usando teclados windows e IMEs (editores de método de entrada) estão em conformidade com a normalização do formulário C definida no padrão Unicode, a conversão de dados de entrada de outras plataformas usando as funções de normalização Unicode do NLS fornece resultados mais consistentes, especialmente para localidades que usam o script tibetano ou o script Hangul para Hangul moderno. Para obter mais informações sobre o suporte à normalização Unicode no Windows Vista e posteriores, consulte Usando a normalização Unicode para representar cadeias de caracteres.

Quando a comparação de cadeia de caracteres segue a preferência de idioma do usuário, por exemplo, ao classificar itens para um controle ListView ordenado, o aplicativo pode fazer um dos seguintes procedimentos:

  • Chame lstrcmp ou lstrcmpi com a localidade do usuário.
  • Chame CompareString ou CompareStringEx para definir uma localidade para a comparação, passar sinalizadores adicionais, inserir caracteres nulos ou passar comprimentos explícitos para corresponder a partes de uma cadeia de caracteres.

Quando os resultados da comparação devem ser consistentes independentemente da localidade, por exemplo, ao comparar dados recuperados com uma lista predefinida ou um valor interno, o aplicativo deve usar CompareString ou CompareStringEx com o parâmetro Locale definido como LOCALE_INVARIANT. Para CompareString, qualquer uma das chamadas a seguir corresponderá mesmo se mystr for "INLAP". Nesse caso, uma chamada sensível à localidade para lstrcmpi falhará se a localidade atual for vietnamita.

No Windows XP:

int iReturn = CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);

Em sistemas operacionais anteriores:

DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
int iReturn = CompareString(lcid, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);

Classificar cadeias de caracteres ordinally

Para classificação ordinal (não linguística), seus aplicativos sempre devem usar a função CompareStringOrdinal .

Observação

Essa função só está disponível para o Windows Vista e posteriores.

 

CompareStringOrdinal compara duas cadeias de caracteres Unicode para testar a igualdade binária, em vez da igualdade linguística. Exemplos dessas cadeias de caracteres não linguísticas são nomes de arquivo NTFS, variáveis de ambiente e nomes de mutexes, pipes nomeados ou maillots. Exceto pela opção de diferenciação de maiúsculas de minúsculas, essa função ignora todas as equivalências não binárias. Ao contrário de algumas outras funções de classificação, ela testa todos os pontos de código para igualdade, incluindo aqueles que não recebem nenhum peso em esquemas de classificação linguística.

Todas as instruções a seguir se aplicam a CompareStringOrdinal em comparações binárias, mas não a CompareString, CompareStringEx, lstrcmp ou lstrcmpi.

  • Sequências canonicamente equivalentes no Unicode, como LATIN SMALL LETTER A WITH RING ABOVE (U+00e5) e LATIN SMALL LETTER A + COMBINING RING ABOVE (U+0061 U+030a), não são iguais mesmo que pareçam idênticas ("å").
  • Cadeias de caracteres canonicamente semelhantes em Unicode, como LATIN LETTER SMALL CAPITAL Y (U+028f) e LATIN CAPITAL LETTER Y (U+0059), que são muito semelhantes ("ʏ" e "Y") e variam apenas por alguns pesos de maiúsculas e minúsculas especiais nas tabelas linguísticas, são consideradas caracteres totalmente diferentes. Mesmo que o aplicativo defina bIgnoreCase como TRUE, essas cadeias de caracteres se comparam como diferentes.
  • Os pontos de código definidos, mas que não têm peso de classificação linguística, como ZERO WIDTH JOINER (U+200d), são tratados como tendo seus pesos de ponto de código.
  • Os pontos de código definidos em versões posteriores do Unicode, mas que não têm peso nas tabelas linguísticas atuais, são tratados como tendo seus pesos de ponto de código.
  • Os pontos de código indefinidos pelo Unicode são tratados como tendo seus pesos de ponto de código.
  • Quando o aplicativo define bIgnoreCase como TRUE, a função mapeia o caso usando a tabela de maiúsculas do sistema operacional, em vez das informações nas tabelas de classificação linguística. Portanto, o mapeamento é independente da localidade.

Para obter mais informações sobre sequências canonicamente equivalentes em Unicode e cadeias de caracteres canonicamente semelhantes em Unicode, consulte Usando a normalização Unicode para representar cadeias de caracteres.

Classificar pontos de código

Alguns pontos de código Unicode não têm peso, por exemplo, ZERO WIDTH NON JOINER, U+200c. As funções de classificação avaliam intencionalmente os pontos de código sem peso como equivalentes porque não têm peso na classificação. No Windows Vista e posterior, o aplicativo pode classificar esses pontos de código chamando as funções de comparação de cadeia de caracteres NLS, particularmente CompareStringOrdinal, para avaliação de todos os pontos de código em um sentido literal binário, por exemplo, na validação de senha. Em sistemas operacionais pré-Windows Vista, o aplicativo deve usar a função de runtime C strcmp ou wcscmp.

As funções de classificação ignoram diacríticos, como NON SPACING BREVE, U+0306, quando o aplicativo especifica o sinalizador hlink_NONSPACE. Da mesma forma, essas funções ignoram símbolos, por exemplo, EQUALS SIGN, U+003d , quando o sinalizador hlink_SYMBOLS é especificado. No Windows Vista e posteriores, o aplicativo chama CompareStringOrdinal para avaliação de diacríticos e pontos de código de símbolo em um sentido literal binário. Em sistemas operacionais pré-Windows Vista, o aplicativo deve usar strcmp ou wcscmp.

Alguns pontos de código, como 0xFFFF e 0x058b, atualmente não são atribuídos no Unicode. Esses pontos de código não recebem peso na classificação e nunca devem ser passados para as funções de classificação. O aplicativo deve usar IsNLSDefinedString para detectar pontos de código não Unicode em um fluxo de dados.

Observação

Os resultados de IsNLSDefinedString podem variar dependendo da versão Unicode passada se um caractere for adicionado ao Unicode em uma versão posterior e posteriormente for adicionado às tabelas de classificação do Windows. Para obter mais informações, consulte Usar controle de versão de classificação.

 

Classificar dígitos como números

No Windows 7 e posterior, o aplicativo pode chamar CompareString, CompareStringEx, LCMapString ou LCMapStringEx usando o sinalizador SORT_DIGITSASNUMBERS. Esse sinalizador dá suporte à classificação que trata dígitos como números, por exemplo, classificação de "2" antes de "10".

Observe que o uso desse sinalizador não é apropriado para dígitos hexadecimais, como os seguintes.

01AF
1BCD
002A
12FA
AB1C
AB02
AB12

Nesse caso, os "números" são classificados em ordem, mas o usuário percebe uma lista hexadecimal mal classificada.

Cadeias de caracteres de mapa

O aplicativo usa a função LCMapString ou LCMapStringEx para mapear cadeias de caracteres, se LCMAP_SORTKEY não for especificado. Uma cadeia de caracteres mapeada será terminada em nulo se a cadeia de caracteres de origem for terminada em nulo.

Ao transformar entre maiúsculas e minúsculas, a função não garante que um único caractere será mapeado para um único caractere. Por exemplo, os sinalizadores LCMAP_LOWERCASE e LCMAP_UPPERCASE podem mapear o Sharp S alemão ("ß") para si mesmo. Como alternativa, o sinalizador LCMAP_UPPERCASE pode mapear "ß" para "SS" e o sinalizador LCMAP_LOWERCASE pode mapear "SS" para "ß". O comportamento depende da versão do NLS.

Ao transformar entre maiúsculas e minúsculas, a função não é sensível ao contexto. Por exemplo, enquanto o sinalizador LCMAP_UPPERCASE mapeia corretamente o sigma minúsculo grego ("σ") e o sigma final em letras minúsculas gregas ("ς") para sigma maiúsculo grego ("Σ"), o sinalizador LCMAP_LOWERCASE sempre mapeia "Σ" para "σ", nunca para "ς".

Por padrão, a função mapeia o "i" minúsculo para o "I" maiúsculo, mesmo quando o parâmetro Locale especifica turco ou azerbaijão. Para substituir esse comportamento para turco ou azerbaijão, o aplicativo deve especificar LCMAP_LINGUISTIC_CASING. Se esse sinalizador for especificado com a localidade apropriada, "ı" (minúsculo sem ponto I) será a forma minúscula de "I" (I sem ponto maiúsculo) e "i" (minúsculas pontilhadas I) será a forma minúscula de "İ" (letras maiúsculas pontilhadas I).

Se o sinalizador LCMAP_HIRAGANA for especificado para mapear caracteres katakana para caracteres hiragana e LCMAP_FULLWIDTH não for especificado, LCMapString ou LCMapStringEx mapeará apenas caracteres de largura total para hiragana. Nesse caso, todos os caracteres katakana de meia largura são colocados como na cadeia de caracteres de destino, sem mapeamento para hiragana. O aplicativo deve especificar LCMAP_FULLWIDTH para mapear caracteres katakana de meia largura para hiragana. O motivo dessa restrição é que todos os caracteres hiragana são caracteres de largura total.

Se o aplicativo precisar remover caracteres da cadeia de caracteres de origem, ele poderá chamar a função de mapeamento com os sinalizadores NORM_IGNORESYMBOLS e NORM_IGNORENONSPACE definidos e todos os outros sinalizadores limpos. Se o aplicativo fizer isso com uma cadeia de caracteres de origem que não é terminada em nulo, é possível que a função retorne uma cadeia de caracteres vazia e não retorne um erro.

Criar chaves de classificação

Quando o aplicativo especifica LCMAP_SORTKEY, LCMapString ou LCMapStringEx gera uma chave de classificação, uma matriz binária de valores de bytes. A chave de classificação não é uma cadeia de caracteres verdadeira e seus valores representam o comportamento de classificação da cadeia de caracteres de origem, mas não são valores de exibição significativos.

Observação

A função ignora o kashida árabe durante a geração de uma chave de classificação. Se um aplicativo chamar a função para criar uma chave de classificação para uma cadeia de caracteres que contém um kashida árabe, a função não criará nenhum valor de chave de classificação.

 

A chave de classificação pode conter um número ímpar de bytes. O sinalizador LCMAP_BYTEREV reverte apenas um número par de bytes. O último byte (ímpar posicionado) na chave de classificação não é invertido. Se o byte de 0x00 terminação for um byte de posição ímpar, ele permanecerá o último byte na chave de classificação. Se o byte de 0x00 terminação for um byte posicionado uniformemente, ele trocará posições com o byte que o precede.

Ao gerar a chave de classificação, a função trata o hífen e o apóstrofo de forma diferente de outros símbolos de pontuação, para que palavras como "coop" e "cooperação" permaneçam juntas em uma lista. Todos os símbolos de pontuação diferentes do hífen e do apóstrofo classificam antes dos caracteres alfanuméricos. O aplicativo pode alterar esse comportamento definindo o sinalizador SORT_STRINGSORT, conforme descrito em Funções de Classificação.

Quando usada no memcmp, a chave de classificação produz a mesma ordem de quando a cadeia de caracteres de origem é usada em CompareString ou CompareStringEx. A função memcmp deve ser usada em vez de strcmp, pois a chave de classificação pode ter bytes nulos inseridos.

Usar controle de versão de classificação

Uma tabela de classificação tem dois números que identificam sua versão: a versão definida e a versão nls. Ambos os números são valores DWORD, compostos por um valor principal e um valor secundário. O primeiro byte de um valor é reservado, os dois próximos bytes representam a versão principal e o último byte representa a versão secundária. Em termos hexadecimais, o padrão é 0xRRMMMMmm, em que R é igual a Reserved, M é igual a major e m é igual a minor. Por exemplo, uma versão principal de 3 com uma versão secundária de 4 é representada como 0x304.

A versão definida identifica o repertório de pontos de código e é a mesma para todas as localidades. A versão principal incrementa para indicar alterações em pontos de código existentes. A versão secundária incrementa para indicar que pontos de código foram adicionados, mas que nenhum ponto de código existente anteriormente foi alterado.

A versão do NLS é específica para um identificador de localidade ou nome de localidade e rastreia as alterações nos pesos do ponto de código para a localidade afetada. A versão principal incrementa quando os pesos são alterados para pontos de código que já eram classificáveis. A versão secundária incrementa quando novos pontos de código são atribuídos pesos, mas todos os outros pesos de ponto de código classificáveis anteriormente permanecem inalterados.

Observação

Para uma versão principal, um ou mais pontos de código são alterados para que o aplicativo precise indexar novamente todos os dados para que as comparações sejam válidas. Para uma versão secundária, nada é movido, mas os pontos de código são adicionados. Para esse tipo de versão, o aplicativo só precisa reindexar cadeias de caracteres com valores anteriormente não variados.

 

Importante

A versão principal foi alterada em Windows 8. Os dados criados em versões anteriores do Windows devem ser indexados novamente.

 

As versões definidas e NLS se aplicam a pontos de código classificáveis recuperados usando a função LCMapString ou LCMapStringEx com o sinalizador LCMAP_SORTKEY e também usadas pelas funções CompareString, CompareStringEx, FindNLSString e FindNLSStringEx . Se um ou mais pontos de código em uma cadeia de caracteres não forem variados, a função IsNLSDefinedString retornará FALSE quando essa cadeia de caracteres for passada para ela como um parâmetro.

O aplicativo pode chamar GetNLSVersion ou GetNLSVersionEx para recuperar a versão definida e a versão do NLS para uma tabela de classificação.

Indexar o banco de dados

Por motivos de desempenho, o aplicativo deve seguir este procedimento ao indexar o banco de dados.

Para indexar corretamente o banco de dados

  1. Para cada função, armazene a versão do NLS, as chaves de classificação dessa versão e uma indicação de classificação para cada cadeia de caracteres indexada.
  2. Quando a versão secundária for incrementada, reindexe cadeias de caracteres anteriormente não classificadas. As cadeias de caracteres afetadas nesta atualização devem ser confinadas às que IsNLSDefinedString retornou anteriormente FALSE.
  3. Quando a versão principal incrementa, reindexe todas as cadeias de caracteres porque os pesos atualizados podem alterar o comportamento de qualquer cadeia de caracteres. As versões principais são muito pouco frequentes.

Problemas de indexação de banco de dados podem surgir pelos seguintes motivos:

  • Um sistema operacional posterior pode definir pontos de código indefinidos para um sistema operacional anterior, alterando assim a classificação.
  • Os pontos de código podem ter pesos de classificação diferentes em sistemas operacionais diferentes, devido a correções no suporte a idiomas.

Para minimizar a necessidade de reindexar o banco de dados nessas circunstâncias, o aplicativo pode usar IsNLSDefinedString para diferenciar definido de cadeias de caracteres indefinidas para que o aplicativo possa rejeitar cadeias de caracteres com pontos de código indefinidos. O uso de GetNLSVersion ou GetNLSVersionEx permite que o aplicativo determine se uma alteração de NLS afeta a localidade usada para uma tabela de índice específica. Se a alteração não tiver nenhum efeito sobre a localidade, o aplicativo não precisará indexar novamente a tabela.

Exemplos

A tabela a seguir ilustra os efeitos de determinados sinalizadores usados com as funções de classificação. Em cada caso, a seleção de sinalizadores determina se dois caracteres diferentes são considerados iguais para fins de classificação.

Caractere 1 Caractere 2 Padrão NORM_IGNOREWIDTH NORM_IGNOREKANA NORM_IGNOREWIDTH| NORMIGNOREKANA
"あ"
U+3042 HIRAGANA LETRA A
"ガ"
U+30A2 KATAKANA LETRA A
Desigual Desigual Igual Igual
"オ"
U+FF75 HALFWIDTH KATAKANA LETTER O
"オ"
U+30AAA KATAKANA LETTER O
Desigual Igual Desigual Igual
"B"
U+FF22 FULLWIDTH LETRA MAIÚSCULA LATINA B
"B"
U+0042 LETRA MAIÚSCULA LATINA B
Desigual Igual Desigual Igual

 

Usando o Suporte à Linguagem Nacional

Classificação

Recuperando e definindo informações de localidade

Usando a normalização unicode para representar cadeias de caracteres

Considerações sobre segurança: recursos internacionais

CompareString

CompareStringEx

CompareStringOrdinal

FindNLSString

FindNLSStringEx

Lcmapstring

LCMapStringEx