Intercalación de cadenas UTF-8 insensible a mayúsculas y minúsculas para SQLite(C/C++)
sqlite website (6)
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 una función de intercalación personalizada en SQLite .
- El método idealmente debería ser independiente de la configuración regional. 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.
- Las opciones incluyen el uso de una biblioteca estándar de C o C ++ o una biblioteca de terceros pequeña (adecuada para el sistema incorporado) y no GPL (adecuada para un sistema propietario).
Lo que tengo hasta ahora
-
strcoll
con C locales ystd::collate_byname
/std::collate_byname
distinguen entre mayúsculas y minúsculas. (¿Hay versiones de éstos insensibles a mayúsculas y minúsculas?) Traté de usar un strcasecmp POSIX, pero parece no estar definido para otras configuraciones que no sean
"POSIX"
En la configuración regional POSIX, strcasecmp () y strncasecmp () hacen 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 entre configuraciones regionales 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 está impreso:
strcasecmp(''Äaa'', ''äaa'') == -32 strcoll(''Äaa'', ''äaa'') == -32 strcasecmp(''Äaa'', ''äaa'') == -32 strcoll(''Äaa'', ''äaa'') == 7 strcasecmp(''Äaa'', ''äaa'') == -32 strcoll(''Äaa'', ''äaa'') == 7
PD
Y sí, estoy al tanto de la UCI , pero no podemos usarla en la plataforma integrada debido a su enorme tamaño .
No tengo una respuesta definitiva en forma de código de ejemplo, pero debo señalar que un byte de UTF-8 contiene, de hecho, caracteres Unicode y debe usar las versiones wchar_t de la biblioteca de tiempo de ejecución de C / C ++.
Sin embargo, primero debes 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 está muy bien documentado . Lo sé, porque lo hice, pero no puedo compartir ese código contigo.
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 Unicode completa 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 largo ).
Creo que necesitarás hacer tuyos 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.
En Windows, puede devolver 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 los componentes internacionales de IBM para Unicode .
Lo que realmente quieres 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 adicional 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 varios 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 * es UTF-8, no puede usar este truco en absoluto. (Según tengo entendido, el CRT de Microsoft sufre esto. Su código multibyte 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 *.
Una vez hecho esto, básicamente puede escribir su propio pedido convirtiendo cadenas a 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?
Si lo está usando para hacer búsquedas y ordenar únicamente su configuración regional, sugiero que su función invoque una función de reemplazo simple que convierta ambas cadenas de bytes múltiples en un byte por caracteres usando una tabla como:
A -> a
à -> a
á -> a
ß -> ss
Ç -> c
y así
Luego simplemente llame a strcmp y devuelva los resultados.