2012-04-27 13 views
11

Necesito realizar una SELECCIONAR consultas que son insensibles a mayúsculas y minúsculas. Para fines de demostración, se crea una tabla como la que:¿Cómo lidiar con caracteres acentuados en iOS SQLite?

create table table 
(
    column text collate nocase 
); 

insert into table values ('A'); 
insert into table values ('a'); 
insert into table values ('Á'); 
insert into table values ('á'); 

create index table_cloumn_Index 
    on table (column collate nocase); 

Entonces, consigo esos resultados al ejecutar las siguientes consultas:

SELECT * FROM table WHERE column LIKE 'a'; 
> A 
> a 

SELECT * FROM table WHERE column LIKE 'á'; 
> á 

SELECT * FROM table WHERE column LIKE 'Á'; 
> Á 

¿Cómo puedo arreglar eso por lo que los resultados de cualquiera de los siguientes las consultas serán así:

> A 
> a 
> Á 
> á 

El sqlite se está ejecutando en iOS, por cierto.

Gracias de antemano,

Respuesta

18

dos enfoques básicos:

  1. Se puede crear una segunda columna de la tabla que contiene la cadena sin los caracteres internacionales. Además, antes de hacer una búsqueda en esta columna de búsqueda secundaria, también debe eliminar los caracteres internacionales de la cadena que está buscando (de esa manera está comparando no internacional con no internacional).

    Esta es la rutina que utilizo para convertir los caracteres internacionales:

    NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; 
    string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
    

    También puede sustituir los caracteres acentuados con:

    NSMutableString *mutableString = [string mutableCopy]; 
    CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO); 
    

    Por cierto, si usted necesita para ordenar los resultados , también puede ordenar en este campo de búsqueda secundaria en lugar del campo principal, lo que evitará los problemas derivados de la incapacidad de SQLite para ordenar los caracteres internacionales.

  2. Puede crear alternativamente su propia función "sin acento" C (definir esta función C fuera del @implementation para su clase):

    void unaccented(sqlite3_context *context, int argc, sqlite3_value **argv) 
    { 
        if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_TEXT) { 
         sqlite3_result_null(context); 
         return; 
        } 
    
        @autoreleasepool { 
         NSMutableString *string = [NSMutableString stringWithUTF8String:(const char *)sqlite3_value_text(argv[0])]; 
         CFStringTransform((__bridge CFMutableStringRef)string, NULL, kCFStringTransformStripCombiningMarks, NO); 
         sqlite3_result_text(context, [string UTF8String], -1, SQLITE_TRANSIENT); 
        } 
    } 
    

    continuación, se puede definir una función de SQLite que llamar a este C-función (llamar a este método después de abrir la base de datos, que será efectiva hasta que se cierre la base de datos):

    - (void)createUnaccentedFunction 
    { 
        if (sqlite3_create_function_v2(database, "unaccented", 1, SQLITE_ANY, NULL, &unaccented, NULL, NULL, NULL) != SQLITE_OK) 
         NSLog(@"%s: sqlite3_create_function_v2 error: %s", __FUNCTION__, sqlite3_errmsg(database)); 
    } 
    

    Una vez hecho esto, ahora puede usar esta nueva función unaccented en SQL, por ejemplo:

    if (sqlite3_prepare_v2(database, "select a from table where unaccented(column) like 'a'", -1, &statement, NULL) != SQLITE_OK) 
        NSLog(@"%s: insert 1: %s", __FUNCTION__, sqlite3_errmsg(database)); 
    
+0

Sí, yo estaba usando este enfoque también, pero ahora Necesito tratar con una base de datos que no puedo cambiar su estructura. Pero si puedes, y tu DB no es demasiado grande, es una manera fácil de hacerlo. –

+0

@LeandroAlves Sé que ya pasó de largo este problema, pero (a) incluyo otra variación para las cadenas que no se acentúan; y (b) le mostrará cómo hacer cadenas de caracteres no acentuadas en SQLite sobre la marcha. – Rob

+0

@Rob El código para la Opción 2 se ejecuta y maneja los datos correctamente, pero lleva la CPU al 99% y la Memoria> 1000 MB. La aplicación falla con 'EXC_BAD_ACCESS (código = 2, dirección = 0x0)' inmediatamente después de completar las operaciones. ¿Hay algo que se pueda hacer para controlar el uso de la memoria con este código? –

4

Tendrá que sea create some user function, o de anulación (es decir, reemplazar) la implementación predeterminada de like() functions. La razón es que el operador LIKE en SQLite no soporta no ASCII caso insensibilidad: Sólo

SQLite entiende mayúsculas/minúsculas para los caracteres ASCII por defecto. El operador LIKE distingue entre mayúsculas y minúsculas para los caracteres de Unicode que están más allá del rango ASCII. Por ejemplo, la expresión 'a' LIKE 'A' es VERDADERA pero 'æ' LIKE 'Æ' es FALSE.

Esto tiene sentido, de lo contrario sqlite necesitaría soportar diferentes culturas ya que el caso varía de una a la otra. Un ejemplo es the capital i in Turkey which is not I but a dotted İ, and the lower-case of I is a dot-less ı. Incrustar toda esta información de cultura en sqlite sería muy engorroso (es decir, aumentaría el código de objeto sqlite).

+0

¿Ya creó alguna función de usuario en iOS? ¿Tienes algún código de muestra sobre eso? Esta parece ser la manera más adecuada de hacer esto. –

1

Aquí está mi solución de LIKE problema

static void myLow(sqlite3_context *context, int argc, sqlite3_value **argv) 
{ 
    NSString* str = [[NSString alloc] initWithUTF8String: 
          (const char *)sqlite3_value_text(argv[0])]; 
    const char* s = [[str lowercaseString] UTF8String]; 
    sqlite3_result_text(context, s, strlen(s), NULL); 
    [str release]; 
} 

// call it once after opening db 
sqlite3_create_function(_db, "myLow", 1, SQLITE_UTF8,NULL, &myLow, NULL, NULL); 

Y entonces, en lugar de la consulta

SELECT * FROM table WHERE column LIKE 'a' 

se debe utilizar

SELECT * FROM table WHERE myLow(column) LIKE 'a' 
Cuestiones relacionadas