2008-10-08 24 views
7

我正在尋找一種方法來以大小寫不敏感的方式比較和排列C++中的UTF-8字符串以在custom collation function in SQLite中使用它。用於SQLite的不區分大小寫的UTF-8字符串整理(C/C++)

  1. 該方法應該理想情況下是區域獨立的。但是,就我所知,我不會屏住呼吸,因爲排序規則是非常依賴語言的,所以任何對英語以外的語言都適用的東西都可以,即使它意味着切換語言環境。
  2. 選項包括使用標準C或C++庫或小型(適用於嵌入式系統)和非GPL(適用於專有系統)的第三方庫。

我有什麼至今:

  1. strcoll使用C語言環境和std::collate/std::collate_byname是區分大小寫的。 (是否有這些不區分大小寫的版本?)
  2. 我試圖使用POSIX strcasecmp,但它似乎是not defined"POSIX"

    在POSIX語言環境,strcasecmp其他區域設置()和strncasecmp()從上到下進行轉換,然後進行字節比較。結果在其他語言環境中未指定。

    而且,事實上,中strcasecmp結果沒有語言環境之間在Linux上用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)); 
    } 
    

    這會打印:

    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

是的,我知道關於ICU,但我們不能用它在嵌入式平臺上,由於其enormous size

回答

7

你真正想要的是邏輯上不可能的。沒有語言環境獨立,不區分大小寫的字符串排序方式。簡單的反例是「我」<>「我」?天真的答案是否定的,但在土耳其這些字符串是不平等的。 「我」被大寫爲「İ」(U + 130 Latin Capital I,帶點在上面)

UTF-8字符串給問題增加了額外的複雜性。如果您有適當的語言環境,則它們是完全有效的多字節字符*字符串。但是C和C++標準都沒有定義這樣的語言環境;檢查你的供應商(太多嵌入式供應商,對不起,這裏沒有專門的答案)。所以你必須選擇一個多字節編碼爲UTF-8的語言環境,以使mbscmp函數起作用。這當然會影響排序順序,這與語言環境有關。如果你沒有const char *爲UTF-8的locale,你根本不能使用這個技巧。 (據我瞭解,微軟的CRT受此影響,他們的多字節代碼只能處理高達2個字節的字符; UTF-8需要3個)

wchar_t也不是標準解決方案。它應該是如此之廣,以至於您不必處理多字節編碼,但您的整理仍將取決於區域設置(LC_COLLATE)。但是,使用wchar_t意味着您現在選擇不使用UTF-8作爲const char *的語言環境。

完成此操作後,您可以基本上通過將字符串轉換爲小寫字母並將它們進行比較來編寫自己的排序。這並不完美。你期望L「ß」== L「ss」嗎?它們的長度不一樣。然而,對於德國人來說,你必須認爲他們是平等的。你能忍受嗎?

+2

關於德國「ß」字符(以及所有如此豐富的案例)的例子:這些字符必須已經被「解決」或以其他方式處理過數千次,UTF-8或否。 MS Word一直有一個「切換大小寫」功能 - 它在Unicode之前的版本中是如何工作的? WordPerfect如何? 我有和OP一樣的問題,除了我在Delphi工作。我見過很多基於Windows sqlite的應用程序,它們執行不區分大小寫的SELECT(我猜ORDER BY),無論它們是以英語,德語還是(在我的情況下)波蘭語區域安裝。試試Firefox :)他們如何做到這一點? – 2009-10-17 23:19:23

0

我不認爲有一個標準的C/C++庫函數可以使用。您必須自行推出或使用第三方庫。可以在這裏找到用於區域特定歸類的完整Unicode規範:http://www.unicode.org/reports/tr10/警告:這是一個文檔)。

0

在Windows上,您可以調用OS函數CompareStringW並使用NORM_IGNORECASE標誌。您必須先將UTF-8字符串轉換爲UTF-16。否則,請看IBM的International Components for Unicode

0

我相信你會需要推出自己的或使用第三方庫。我建議第三方圖書館,因爲有很多規則需要遵循才能獲得真正的國際支持 - 最好是讓專家處理他們。

0

我沒有以示例代碼的形式給出明確的答案,但我應該指出,UTF-8字節流實際上包含Unicode字符,並且您必須使用C/C++運行時庫的wchar_t版本。

但是,您必須首先將這些UTF-8字節轉換爲wchar_t字符串。這並不難,因爲UTF-8編碼標準是very well documented。我知道這一點,因爲我已經做到了,但我無法與您分享這些代碼。

0

如果你使用它做搜索和排序的只有你的語言環境,我建議你的函數調用一個簡單的替換使用表既像多字節字符串轉換成每個字符的人一個字節的功能:

A - >一個
A - >一個
A - >一個
SS - > SS
ç - >ç

然後簡單地調用的strcmp並返回結果。

相關問題