2012-04-27 7 views
11

Ich muss eine SELECT-Abfragen ausführen, die auf Groß-/Kleinschreibung und Akzente nicht reagieren. Für Demozwecke, erstelle ich eine Tabelle wie folgt aus:Wie behandelt man Akzentzeichen in 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); 

Dann habe ich diese Ergebnisse erhalten, wenn die folgenden Abfragen ausführen:

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

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

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

Wie kann ich das beheben, dass so die Ergebnisse für eine der folgenden Abfragen aussehen:

> A 
> a 
> Á 
> á 

die sQLite auf iOS läuft, durch die Art und Weise.

Vielen Dank im Voraus,

Antwort

18

Zwei grundlegende Ansätze:

  1. Sie können eine zweite Spalte in der Tabelle erstellen, die die Zeichenfolge ohne die internationalen Zeichen enthalten. Außerdem sollten Sie vor der Suche nach dieser sekundären Suchspalte auch internationale Zeichen aus der gesuchten Zeichenfolge entfernen (auf diese Weise vergleichen Sie nicht-internationale mit nicht-internationalen).

    Dies ist die Routine, die ich die internationale Zeichen zu konvertieren verwenden:

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

    Sie auch die Zeichen mit Akzent mit ersetzen könnte:

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

    By the way, wenn Sie Ihre Ergebnisse sortieren müssen Sie können auch nach diesem sekundären Suchfeld statt nach dem Hauptfeld sortieren, wodurch Probleme vermieden werden, die sich aus der Unfähigkeit von SQLite ergeben, die internationalen Zeichen zu sortieren.

  2. Sie können alternativ Ihre eigene „unbetonten“ C-Funktion (definieren diese C-Funktion außerhalb der @implementation für Ihre Klasse) erstellen:

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

    Sie können dann eine SQLite-Funktion definieren, die diese C-Funktion aufrufen wird (rufen Sie diese Methode, nachdem Sie die Datenbank öffnen, die wirksam sein wird, bis Sie die Datenbank schließen):

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

    das getan haben, Sie nun diese neue unaccented Funktion in SQL verwenden können, zum Beispiel:

    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

Ja verwenden sollte, auch ich diesen Ansatz wurde mit, aber jetzt Ich muss mich mit einer Datenbank beschäftigen, deren Struktur ich nicht ändern kann. Aber wenn Sie können, und Ihre dB ist nicht zu groß, es ist ein einfacher Weg zu gehen. –

+0

@LeandroAlves Ich weiß, dass Sie dieses Problem schon lange hinter sich gebracht haben, aber ich (a) schließe eine andere Variante ein, um nicht erkennbare Strings zu erzeugen; und (b) zeigen Ihnen, wie Sie unaccent Strings in SQLite im laufenden Betrieb. – Rob

+0

@Rob Der Code für Option 2 führt die Daten korrekt aus und verarbeitet sie, aber sie nimmt die CPU auf 99% und den Arbeitsspeicher> 1000 MB. Die Anwendung stürzt unmittelbar nach Abschluss der Operationen mit 'EXC_BAD_ACCESS (Code = 2, Adresse = 0x0) ab. Gibt es etwas, das getan werden kann, um die Speichernutzung mit diesem Code zu kontrollieren? –

4

Sie müssen entweder create some user function oder Überschreibung (d ersetzen) die Standardimplementierung der like() functions. Der Grund dafür ist, dass die LIKE Betreiber in SQLite nicht Nicht-ASCII-Fall-Unempfindlichkeit unterstützt:

nur

SQLite versteht von Standard Groß-/Kleinschreibung für ASCII-Zeichen. Der LIKE-Operator unterscheidet standardmäßig die Unicode-Zeichen , die außerhalb des ASCII-Bereichs liegen. Zum Beispiel ist der Ausdruck 'a' LIKE 'A' wahr, aber 'æ' LIKE 'is' ist FALSE.

Das macht Sinn, ansonsten müsste sqlite verschiedene Kulturen unterstützen, da der Fall von Fall zu Fall variiert. Ein Beispiel ist the capital i in Turkey which is not I but a dotted İ, and the lower-case of I is a dot-less ı. Das Einbetten all dieser Kulturinformationen in sqlite wäre sehr mühsam (d. H. Es würde den SQL-Objektcode erhöhen).

+0

Haben Sie bereits eine Benutzerfunktion auf iOS erstellt? Haben Sie einen Beispielcode dazu? Dies scheint der bessere Weg zu sein. –

1

Hier ist meine Lösung von LIKE Problem

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

Und dann statt Abfrage

SELECT * FROM table WHERE column LIKE 'a' 

Sie

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