2008-10-08 8 views
7

Ich bin auf der Suche nach einer Methode zum Vergleichen und Sortieren von UTF-8-Zeichenfolgen in C++ in einer Groß-und Kleinschreibung, um es in einer custom collation function in SQLite zu verwenden.Fall-insensitive UTF-8-String-Sortierung für SQLite (C/C++)

  1. Die Methode sollte idealerweise ortsunabhängig sein. Ich werde jedoch nicht den Atem anhalten, soweit ich weiß, ist die Sortierung sehr sprachabhängig, so dass alles, was mit anderen Sprachen als Englisch zu tun hat, auch dann funktioniert, wenn es bedeutet, das Gebietsschema zu wechseln.
  2. Optionen gehören Standard-C oder C++ Bibliothek oder eine kleine (geeignet für Embedded System) und nicht-GPL (geeignet für ein proprietäres System) BIBLIOTHEK Dritter.

Was ich habe, so weit:

  1. strcoll mit C locales und std::collate/std::collate_byname sind case-sensitive. (Gibt es Groß- und Kleinschreibung Versionen davon?)
  2. Ich versuchte, einen POSIX strcasecmp zu verwenden, aber es scheint not defined für Gegenden zu sein, andere als "POSIX"

    Im POSIX locale, strcasecmp() und strncasecmp() führt einen höheren Wert für niedrigere Conversions und einen Byte-Vergleich aus. Die Ergebnisse sind in anderen Ländereinstellungen nicht angegeben.

    Und in der Tat ist das Ergebnis strcasecmp nicht zwischen locales auf Linux mit GLIBC ändern.

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

    Dies wird gedruckt:

    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

Und ja, ich bin mir bewusst, über ICU, aber wir können es nicht auf die Embedded-Plattform verwenden, aufgrund seiner enormous size .

Antwort

7

Was Sie wirklich wollen, ist logisch unmöglich. Es gibt keine locale-unabhängige, case-insensitive Art der Sortierung von Strings. Das einfache Gegenbeispiel ist "i" <> "I"? Die naive Antwort ist nein, aber im Türkischen sind diese Strings ungleich. "i" ist auf "İ" (U + 130 Latin Capital I mit Punkt oben)

UTF-8-Strings fügen der Frage zusätzliche Komplexität hinzu. Sie sind perfekt gültige Multi-Byte-Zeichenketten *, wenn Sie ein geeignetes Gebietsschema haben. Aber weder der C- noch der C++ - Standard definiert ein solches Gebietsschema; Erkundigen Sie sich bei Ihrem Anbieter (zu viele eingebettete Anbieter, Entschuldigung, hier keine Antwort). Sie müssen also ein Gebietsschema auswählen, dessen Multi-Byte-Codierung UTF-8 ist, damit die Funktion mbscmp funktioniert. Dies wirkt sich natürlich auf die Sortierreihenfolge aus, die vom Gebietsschema abhängig ist. Und wenn Sie KEIN Gebietsschema haben, in dem const char * UTF-8 ist, können Sie diesen Trick überhaupt nicht verwenden. (Wie ich es verstehe, leidet Microsofts CRT darunter. Ihr Multi-Byte-Code behandelt nur Zeichen bis zu 2 Bytes; UTF-8 benötigt 3)

wchar_t ist auch nicht die Standardlösung. Es ist angeblich so weit, dass Sie sich nicht mit Multi-Byte-Codierungen beschäftigen müssen, aber Ihre Sortierung hängt immer noch vom Gebietsschema ab (LC_COLLATE). Die Verwendung von wchar_t bedeutet jedoch, dass Sie jetzt Gebietsschemata auswählen, die UTF-8 nicht für const char * verwenden.

Mit diesem Schritt können Sie Ihre eigene Bestellung schreiben, indem Sie Zeichenketten in Kleinbuchstaben umwandeln und vergleichen. Es ist nicht perfekt. Erwarten Sie L "ß" == L "ss"? Sie sind nicht gleich lang. Für einen Deutschen muss man sie jedoch als gleich betrachten. Kannst du damit leben?

+2

Über Ihr Beispiel mit dem deutschen "ß" -Zeichen (und alle solche Fälle): diese müssen "gelöst" oder anderweitig tausende Male vorher behandelt worden sein, UTF-8 oder nein. MS Word hatte schon immer eine "toggle case" -Funktion - wie funktionierte es in Unicode-Versionen? Wie hat WordPerfect funktioniert? Ich habe das gleiche Problem wie das OP, außer dass ich in Delphi arbeite. Ich habe eine Reihe von Windows sqlite-basierten Apps gesehen, die SELECT (und ich denke ORDER BY) ohne Unterscheidung der Groß- und Kleinschreibung ausführen, egal ob sie in einem deutschen, deutschen oder (in meinem Fall) polnischen Gebietsschema installiert sind. Probieren Sie Firefox :) Wie machen sie das? –

+0

Normalerweise falsch :) Polnisch hat IIRC keine schweren Fälle; Alle Nicht-ASCII-Zeichen, die in Polnisch verwendet werden, basieren auf "ASCII-Zeichen". – MSalters

+0

Mit Ausnahme des Türkisch I-Problems funktioniert der Unicode Case Folding-Algorithmus (http://www.unicode.org/reports/tr44/) bemerkenswert gut. – dalle

0

Ich glaube nicht, dass es eine Standard C/C++ - Bibliotheksfunktion gibt, die Sie verwenden können. Sie müssen selbst rollen oder eine Bibliothek eines Drittanbieters verwenden. Die vollständige Unicode-Spezifikation für die länderspezifische Sortierung finden Sie hier: http://www.unicode.org/reports/tr10/ (Warnung: Dies ist ein langes Dokument).

0

Unter Windows können Sie auf die OS-Funktion CompareStringW zurückgreifen und das NORM_IGNORECASE-Flag verwenden. Sie müssen zuerst Ihre UTF-8-Zeichenfolgen in UTF-16 konvertieren. Andernfalls werfen Sie einen Blick auf IBM International Components for Unicode.

0

Ich glaube, Sie müssen Ihre eigenen rollen oder verwenden Sie eine Drittanbieter-Bibliothek. Ich empfehle eine Bibliothek von Dritten, da es viele Regeln gibt, die befolgt werden müssen, um echte internationale Unterstützung zu erhalten - am besten, jemanden, der ein Experte ist, mit ihnen zu verhandeln.

0

Ich habe keine definitive Antwort in Form von Beispielcode, aber ich sollte darauf hinweisen, dass ein UTF-8 Bytestream in der Tat Unicode-Zeichen enthält und Sie die wchar_t-Versionen der C/C++ Laufzeitbibliothek verwenden müssen.

Sie müssen diese UTF-8 Bytes jedoch zunächst in wchar_t Zeichenfolgen konvertieren. Dies ist nicht sehr schwierig, da der UTF-8-Codierungsstandard very well documented ist. Ich weiß das, weil ich es getan habe, aber ich kann diesen Code nicht mit dir teilen.

0

Wenn Sie es verwenden Suche zu tun und nur für Ihren Ort sortieren, schlage ich Ihre Funktion eine einfache Funktion, die sowohl Multi-Byte-Zeichenfolgen in ein Byte pro Zeichen diejenigen umwandeln ersetzen rufen Sie eine Tabelle mit wie:

A -> a
A -> a
A -> a
ß -> ss
Ç -> c
und so weiter

Dann einfach strcmp aufrufen und die Ergebnisse zurück.