2008-10-08 9 views
7

Estoy buscando un método para comparar y ordenar cadenas UTF-8 en C++ de una manera insensible a mayúsculas y minúsculas para usarlo en un custom collation function in SQLite.Intercalación de cadenas UTF-8 insensible a mayúsculas/minúsculas para SQLite (C/C++)

  1. El método debería idealmente ser independiente de la Localidad. Sin embargo, no voy a contener la respiración, hasta donde yo sé, la intercalación es muy dependiente del idioma, por lo que todo lo que funciona en idiomas distintos del inglés servirá, incluso si esto significa cambiar de configuración.
  2. opciones incluyen el uso de C estándar o biblioteca de C++ o una pequeña (adecuado para el sistema integrado) y (adecuado para un sistema propietario) biblioteca-GPL no de terceros.

Lo que tengo hasta ahora:

  1. strcoll con lugares C y std::collate/std::collate_byname entre mayúsculas y minúsculas. (¿Hay versiones mayúsculas y minúsculas de estos?)
  2. He intentado utilizar un strcasecmp POSIX, pero parece ser not defined para otras localizaciones distintas a "POSIX"

    En la localización POSIX, strcasecmp() y strncasecmp() hace conversiones de mayor a menor, luego una comparación de bytes. Los resultados no están especificados en otras configuraciones regionales.

    Y, de hecho, el resultado de strcasecmp no cambia de un entorno nacional en Linux con glibc.

    #include <clocale> 
    #include <cstdio> 
    #include <cassert> 
    #include <cstring> 
    
    const static char *s1 = "Äaa"; 
    const static char *s2 = "äaa"; 
    
    int main() { 
        printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
        assert(setlocale(LC_ALL, "en_AU.UTF-8")); 
        printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
        assert(setlocale(LC_ALL, "fi_FI.UTF-8")); 
        printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
    } 
    

    Esto se imprime:

    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == -32 
    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == 7 
    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == 7 
    

PS

Y sí, soy consciente de ICU, pero no podemos usarlo en la plataforma embebida debido a su enormous size .

Respuesta

7

Lo que realmente quiere es lógicamente imposible. No existe una forma de ordenar cadenas independiente de la configuración regional e independiente de mayúsculas y minúsculas. El simple contraejemplo es "i" <> "I"? La respuesta ingenua es no, pero en turco estas cadenas son desiguales. "i" está en mayúscula en "İ" (U + 130 Latín Capital I con punto arriba)

Las cadenas UTF-8 agregan complejidad extra a la pregunta. Son cadenas de caracteres de múltiples bytes perfectamente válidas, si tiene una configuración regional adecuada. Pero ni el estándar C ni el estándar C++ definen dicha configuración regional; verifique con su proveedor (demasiados proveedores integrados, lo siento, no hay respuesta genearl aquí). Por lo tanto, TIENE que elegir una configuración regional cuya codificación de múltiples bytes sea UTF-8, para que funcione la función mbscmp. Esto, por supuesto, influye en el orden de clasificación, que depende de la configuración regional. Y si no tiene NINGUNA configuración regional en la que const char * sea UTF-8, no puede usar este truco en absoluto. (Según tengo entendido, el CRT de Microsoft sufre esto. Su código multi-byte solo maneja caracteres de hasta 2 bytes; UTF-8 necesita 3)

wchar_t tampoco es la solución estándar. Supuestamente es tan amplio que no tiene que lidiar con codificaciones multibyte, pero su colación aún dependerá de la configuración regional (LC_COLLATE). Sin embargo, usar wchar_t significa que ahora eliges locales que no usan UTF-8 para const char *.

Con esto, básicamente puede escribir su propio pedido convirtiendo cadenas en minúsculas y comparándolas. No es perfecto ¿Esperas que L "ß" == L "ss"? Ni siquiera tienen la misma longitud. Sin embargo, para un alemán, debes considerarlos iguales. Puedes vivir con eso?

+2

Sobre su ejemplo con el carácter alemán "ß" (y todos esos casos abundantes): estos deben haber sido "resueltos" o tratados de otra manera miles de veces antes, UTF-8 o no. MS Word siempre ha tenido una función de "alternar mayúsculas y minúsculas". ¿Cómo funcionaba con ese personaje en las versiones anteriores a Unicode? ¿Cómo funciona WordPerfect? Estoy teniendo el mismo problema que el OP, excepto que trabajo en Delphi. He visto varias aplicaciones basadas en sqlite de Windows que realizan SELECT (y creo que ORDER BY) insensible a mayúsculas y minúsculas, independientemente de si están instaladas en una configuración polaca en inglés, alemán o (en mi caso) polaco. Prueba Firefox :) ¿Cómo lo hacen? –

+0

Normalmente incorrecto :) Polaco tiene IIRC sin casos difíciles; todos los caracteres no ASCII utilizados en polaco están "basados ​​en" caracteres ASCII. – MSalters

+0

Excepto por el problema Turco I, el algoritmo Unicode Case Folding (http://www.unicode.org/reports/tr44/) funciona notablemente bien. – dalle

0

No creo que haya una función de biblioteca C/C++ estándar que pueda usar. Tendrás que hacer tu propia o usar una biblioteca de terceros. La especificación completa de Unicode para la intercalación específica de la configuración regional se puede encontrar aquí: http://www.unicode.org/reports/tr10/ (advertencia: este es un documento de largo).

0

En Windows, puede recuperar la función del sistema operativo CompareStringW y utilizar el indicador NORM_IGNORECASE. Primero deberá convertir sus cadenas UTF-8 a UTF-16. De lo contrario, eche un vistazo a IBM's International Components for Unicode.

0

Creo que tendrá que hacer las suyas propias o utilizar una biblioteca de terceros. Recomiendo una biblioteca de terceros porque hay muchas reglas que deben seguirse para obtener un verdadero apoyo internacional; lo mejor es dejar que alguien que sea un experto se encargue de ellos.

0

No tengo una respuesta definitiva en forma de código de ejemplo, pero debo señalar que una UTF-8 bytestream contiene, de hecho, caracteres Unicode y debe usar las versiones wchar_t de la biblioteca C/C++ runtime.

Primero tiene que convertir esos bytes UTF-8 en cadenas wchar_t. Esto no es muy difícil, ya que el estándar de codificación UTF-8 es very well documented. Lo sé, porque lo hice, pero no puedo compartir ese código contigo.

0

Si lo está utilizando para hacer la búsqueda y clasificación por sólo su configuración regional, que sugieren su función para llamar a una simple función que permite convertir las dos cadenas de múltiples bytes en un byte por los carbonilla utilizando una tabla como reemplazar:

a -> a
a -> a
a -> a
ß -> ss
Ç -> c
y así sucesivamente

Después, simplemente llamar a strcmp y devolver los resultados.

Cuestiones relacionadas