2012-04-27 13 views
11

Należy wykonać zapytania SELECT, które są niewrażliwe na wielkość liter i akcenty. Dla celów demonstracyjnych, tworzę tabelę tak:Jak radzić sobie z znaki akcentowane w SQLite iOS?

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); 

Potem dostać te wyniki podczas wykonywania następujących zapytań:

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

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

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

Jak mogę naprawić więc wyniki dla któregokolwiek z następujących pytania są takie:

> A 
> a 
> Á 
> á 

Sqlite działa na iOS, przy okazji.

Dzięki z góry,

Odpowiedz

18

dwa podstawowe podejścia:

  1. Można utworzyć drugą kolumnę w tabeli, która zawiera ciąg bez znaków międzynarodowych. Ponadto przed wykonaniem wyszukiwania w tej dodatkowej kolumnie wyszukiwania należy również usunąć znaki międzynarodowe z szukanego ciągu znaków (w ten sposób porównuje się dane nie-międzynarodowe z nie-międzynarodowymi).

    To rutynowe używam do konwersji międzynarodowych znaków:

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

    Można również zastąpić znaki akcentowane z:

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

    Przy okazji, jeśli chcesz posortować wyniki możesz również sortować to dodatkowe pole wyszukiwania zamiast głównego pola, co pozwoli uniknąć problemów wynikających z niezdolności SQLite do sortowania znaków międzynarodowych.

  2. Można alternatywnie utworzyć własną funkcję „akcentu” c (zdefiniować tę funkcję C poza @implementation dla swojej klasie):

    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); 
        } 
    } 
    

    Następnie można zdefiniować funkcję SQLite, które nazywają to C-funkcji (wywołanie tej metody po otwarciu bazy danych, która będzie obowiązywać do czasu zamknięcia tej bazy):

    - (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)); 
    } 
    

    zrobiwszy, że można teraz korzystać z tej nowej unaccented funkcji w SQL, np:

    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

Tak, byłem przy użyciu tej metody zbyt, ale teraz Muszę zająć się bazą danych, której nie mogę zmienić. Ale jeśli możesz, a twoja db nie jest zbyt duża, jest to prosta droga. –

+0

@ LeandroAlves Wiem, że już dawno temu minęło ten problem, ale ja (a) dołączam kolejną odmianę dla nieakcentujących ciągów; i (b) pokażę ci, jak w locie SQLite w locie nieakcentowane ciągi znaków. – Rob

+0

@Rob Kod opcji 2 uruchamia i obsługuje dane poprawnie, ale zabiera CPU do 99% i Pamięć> 1000 MB. Aplikacja ulega awarii z 'EXC_BAD_ACCESS (kod = 2, adres = 0x0)' natychmiast po zakończeniu operacji. Czy jest coś, co można zrobić, aby kontrolować wykorzystanie pamięci za pomocą tego kodu? –

4

trzeba będzie albo create some user function lub nadpisanie (tzn zamień) domyślną implementację like() functions. Powodem jest to, że operator w SQLite LIKE nie obsługuje non-ASCII orzecznictwa niewrażliwość:

SQLite tylko rozumie dużymi/małymi literami dla znaków ASCII przez domyślnie. Operator LIKE rozróżnia wielkość liter domyślnie dla znaków Unicode , które wykraczają poza zakres ASCII. Na przykład wyrażenie "a" LIKE "A" ma wartość PRAWDA, ale "ę" LIKE "Æ" ma wartość FAŁSZ.

To ma sens, w innym przypadku sqlite musiałby obsługiwać różne kultury, ponieważ wielkość liter różni się w zależności od przypadku. Przykład to the capital i in Turkey which is not I but a dotted İ, and the lower-case of I is a dot-less ı. Osadzenie wszystkich informacji o kulturze w sqlite byłoby bardzo uciążliwe (tj. Spowodowałoby zwiększenie kodu obiektowego sqlite).

+0

Czy utworzyłeś już jakąś funkcję użytkownika w systemie iOS? Czy masz jakiś przykładowy kod na ten temat? Wydaje się, że jest to najcięższy sposób robienia tego. –

1

Oto moje rozwiązanie podobnego problemu

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); 

A potem zamiast zapytania

SELECT * FROM table WHERE column LIKE 'a' 

należy użyć

SELECT * FROM table WHERE myLow(column) LIKE 'a' 
Powiązane problemy