7

SQLiteのカスタム照合関数で使用するために、大文字と小文字を区別しない方法で C++ の UTF-8 文字列を比較およびソートする方法を探しています。

  1. このメソッドは、理想的にはロケールに依存しない必要があります。しかし、私が知る限り、照合は言語に大きく依存しているため、ロケールの切り替えを意味する場合でも、英語以外の言語で動作するものは何でも動作します。
  2. オプションには、標準の C または C++ ライブラリ、または小規模(組み込みシステムに適した) および非 GPL (プロプライエタリ システムに適した) サードパーティ ライブラリの使用が含まれます。

私がこれまでに持っているもの:

  1. strcollC ロケールでは およびstd::collate/std::collate_bynameは大文字と小文字が区別されます。(これらの大文字と小文字を区別しないバージョンはありますか?)
  2. POSIX strcasecmp を使用しようとしましたが、それ以外のロケールでは定義されていないようです"POSIX"

    POSIX ロケールでは、strcasecmp() と strncasecmp() は上位から下位への変換を行い、次にバイト比較を行います。結果は、他のロケールでは指定されていません。

    実際、GLIBC を使用する Linux ではロケール間での結果はstrcasecmp変わりません。

    #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については承知していますが、サイズが非常に大きいため、組み込みプラットフォームでは使用できません。

4

6 に答える 6

7

あなたが本当に望んでいることは、論理的に不可能です。ロケールに依存せず、大文字と小文字を区別しない文字列の並べ替え方法はありません。簡単な反例は "i" <> "I" ? 単純な答えはノーですが、トルコ語ではこれらの文字列は等しくありません。"i" は大文字の "İ" (U+130 ラテン大文字 I 上にドットあり)

UTF-8 文字列を使用すると、質問がさらに複雑になります。適切なロケールがあれば、それらは完全に有効なマルチバイト char* 文字列です。しかし、C 標準も C++ 標準も、そのようなロケールを定義していません。ベンダーに確認してください(組み込みベンダーが多すぎます。申し訳ありませんが、一般的な回答はありません)。そのため、mbscmp 関数を機能させるには、マルチバイト エンコーディングが UTF-8 であるロケールを選択する必要があります。これはもちろん、ロケールに依存するソート順に影響します。また、const char* が UTF-8 であるロケールがない場合、このトリックはまったく使用できません。(私が理解しているように、Microsoft の CRT はこれに悩まされています。彼らのマルチバイト コードは最大 2 バイトの文字しか処理しません。UTF-8 では 3 バイトが必要です)

wchar_t も標準的な解決策ではありません。おそらく非常に広いため、マルチバイトエンコーディングを処理する必要はありませんが、照合はまだロケール (LC_COLLATE) に依存します。ただし、wchar_t を使用すると、const char* に UTF-8 を使用しないロケールを選択することになります。

これが完了すると、文字列を小文字に変換して比較することで、基本的に独自の順序を記述できます。完璧ではありません。L"ß" == L"ss" を期待しますか? それらは同じ長さでさえありません。それでも、ドイツ人にとっては、それらを同等と見なす必要があります。あなたはそれと一緒に暮らすことができますか?

于 2008-10-10T13:28:08.273 に答える
0

それを使用してロケールのみの検索と並べ替えを行う場合は、次のようなテーブルを使用して、両方のマルチバイト文字列を 1 文字あたり 1 バイトに変換する単純な置換関数を呼び出す関数をお勧めし ます


a
á -> a
ß -> ss
Ç -> c
など

次に、単に strcmp を呼び出して結果を返します。

于 2009-02-16T09:37:57.407 に答える
0

使用できる標準の C/C++ ライブラリ関数はないと思います。自分で作成するか、サードパーティのライブラリを使用する必要があります。ロケール固有の照合に関する完全な Unicode 仕様は、次の場所にあります。http://www.unicode.org/reports/tr10/ (警告: これは長い文書です)。

于 2008-10-08T02:15:37.507 に答える
0

コード例の形で決定的な答えはありませんが、UTF-8 バイトストリームには実際には Unicode 文字が含まれており、C/C++ ランタイム ライブラリの wchar_t バージョンを使用する必要があることを指摘しておく必要があります。

ただし、最初にこれらの UTF-8 バイトを wchar_t 文字列に変換する必要があります。UTF-8 エンコーディング標準は十分に文書化されているため、これはそれほど難しいことではありません。私はそれをやったので、私はこれを知っていますが、そのコードをあなたと共有することはできません.

于 2008-10-10T11:50:35.290 に答える
0

Windows では、OS 関数 CompareStringW でフォールバックを呼び出し、NORM_IGNORECASE フラグを使用できます。最初に UTF-8 文字列を UTF-16 に変換する必要があります。それ以外の場合は、IBM のInternational Components for Unicode を参照してください。

于 2008-10-09T12:02:42.717 に答える
0

自分で作成するか、サードパーティのライブラリを使用する必要があると思います。真の国際的なサポートを得るには多くのルールに従う必要があるため、サードパーティのライブラリをお勧めします - 専門家に対処してもらうのが最善です.

于 2008-10-09T13:00:33.647 に答える