2008-10-08 27 views
7

Ben custom collation function in SQLite içinde kullanmak için bir büyük harf duyarlı olmayan şekilde C++ UTF-8 dizeleri karşılaştırmak ve sıralamak için bir yöntem arıyorum.SQLite (C/C++) için büyük/küçük harf duyarsız UTF-8 dize harmanlama

  1. yöntem ideal yerele bağımsız olmalıdır. Ancak, nefesimi tutmayacağım, bildiğim kadarıyla, harmanlama çok dile bağlıdır, bu yüzden İngilizce'yi başka bir dilde çalıştıran herhangi bir şey, yerelleri değiştirmek anlamına gelse bile.
  2. Seçenek C ya da C++ kütüphanesi ya da bir küçük (gömülü sistemi için) ve GPL olmayan (özel bir sistem için uygundur) üçüncü taraf kitaplığı kullanılarak, içermektedir.
  3. var bugüne kadar ne

: C yerel ayarlar ile

  1. strcoll ve std::collate/std::collate_byname harf duyarlıdır.
  2. Bir POSIX strcasecmp kullanmaya çalıştı (bunlardan küçük harf duyarsız versiyonları? Var mıdır), ancak "POSIX" POSIX yerelinde

    , strcasecmp işlevindeki dışındaki yerler için not defined gibi görünüyor() ve strncasecmp(), daha düşük dönüşümleri yapar, daha sonra bayt karşılaştırması yapar. Sonuçlar diğer lokallerde belirtilmemiş.

    Ve, gerçekten, strcasecmp sonucu glibc ile Linux üzerinde yerel ayarlar arasında değişmez.

    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

Ve evet, ben ICU hakkında farkındayım, ancak biz nedeniyle enormous size için gömülü platformda kullanamaz:

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

Bu yazdırılır .

cevap

7

Gerçekten istediğiniz şey mantıken imkansız. Dizeleri sıralamada yerel bağımsız, büyük/küçük harf duyarlı olmayan bir yol yoktur. Basit karşı örnek "i" <> "I"? Naif cevap hayır, ama Türkçe'de bu dizeler eşit değil. "i", "İ" harfine (yukarıdaki noktaya sahip U + 130 Latin Başkenti I), bu soruna fazladan karmaşıklık katar. Uygun bir yerel ayarınız varsa, mükemmel bir şekilde çok baytlık char * dizeleridir. Ancak ne C ne de C++ standardı böyle bir yerel yeri tanımlar; satıcınıza danışın (çok fazla yerleşik satıcı, üzgünüm, hiçbir genearl cevabı yok). Yani, mbscmp işlevinin çalışması için çoklu bayt kodlaması UTF-8 olan bir yerel ayarı seçmeniz gerekiyor. Bu elbette yerel olarak bağımlı olan sıralama düzenini etkiler. Ve eğer const char * UTF-8 olan NO yerel ayarınız yoksa, bu hileyi hiç kullanamazsınız. (Anladığım kadarıyla, Microsoft'un CRT'si bundan muzdarip. Çoklu bayt kodları yalnızca 2 bayta kadar karakterleri işliyor; UTF-8 gereksinimleri 3)

wchar_t, standart çözüm de değildir. Sözüm o kadar geniş ki, çok baytlı kodlamalar ile uğraşmak zorunda değilsiniz, ancak harmanlama hala yerel ayarlara bağlı olacaktır (LC_COLLATE). Ancak, wchar_t kullanımı artık const char * için UTF-8 kullanmayan yerelleri seçmeniz anlamına gelir.

Bu işlem tamamlandığında, dizeleri küçük harfe dönüştürerek ve karşılaştırarak kendi siparişlerinizi yazabilirsiniz. Mükemmel değil. L "ß" == L "ss" yi bekliyor musunuz? Aynı uzunlukta bile değiller. Yine de, bir Alman için onları eşit olarak düşünmelisiniz. Bununla yaşayabilir misin?

:

+2

Örneğiniz, Alman "ß" karakteri (ve bu kadar çok sayıda vaka) ile ilgili olarak: bunlar, "çözülmüş" olmalı ya da UTF-8 veya daha önce binlerce kez daha önce yapılmalıdır. MS Word, her zaman bir "geçiş durumu" özelliğine sahipti - Unicode öncesi sürümlerde bu karakter üzerinde nasıl çalıştı? WordPerfect nasıldı? Delphi'de çalışmak dışında OP ile aynı sorunu yaşıyorum. Bir ingilizce, Almanca veya (benim durumumda) Lehçe yerel ayarında yüklü olsun, büyük/küçük harf duyarlı SELECT (ve SİPARİŞ BY) gerçekleştiren bir dizi Windows sqlite tabanlı uygulamalar gördüm. Firefox'u Dene :) Bunu nasıl yapıyorlar? –

+0

Genellikle yanlış :) Lehçe, IIRC sert vakası yoktur; Lehçe'de kullanılan tüm ASCII olmayan karakterler "ASCII karakterlerine dayanır". – MSalters

+0

Türkçe I sorunu hariç, Unicode Vaka Katlama algoritması (http://www.unicode.org/reports/tr44/) oldukça iyi çalışıyor. – dalle

0

Kullanabileceğiniz standart bir C/C++ kitaplığı işlevi olduğunu sanmıyorum. Kendinizi yuvarlamak veya üçüncü taraf bir kütüphane kullanmak zorundasınız. Yerel olarak özel harmanlama için tam Unicode belirtimi şu adreste bulunabilir: http://www.unicode.org/reports/tr10/ (uyarı: bu, uzun belgesidir).

0

Windows'ta, CompareStringW işletim sistemi işlevine geri dönmeyi ve NORM_IGNORECASE bayrağını kullanabilirsiniz. UTF-8 dizelerinizi önce UTF-16'ya dönüştürmeniz gerekir. Aksi halde, IBM'in International Components for Unicode'a bir göz atın.

0

Kendinizi yuvarlamanız veya bir üçüncü parti kitaplığı kullanmanız gerektiğine inanıyorum. Bir üçüncü parti kütüphanesi tavsiye ederim çünkü gerçek bir uluslararası destek almak için takip edilmesi gereken çok fazla kural var - en iyisi onlarla bir uzmanlık anlaşmasına izin vermek.

0

Örnek kod biçiminde kesin bir cevabım yok, ancak bir UTF-8 bytestream'in aslında Unicode karakterleri içerdiğini ve C/C++ çalışma zamanı kitaplığının wchar_t sürümlerini kullanmanız gerektiğini belirtmeliyim.

Bu UTF-8 baytlarını önce wchar_t dizelerine dönüştürmelisiniz. UTF-8 kodlama standardı very well documented olduğundan bu çok zor değildir. Bunu biliyorum çünkü yaptım ama bu kodu seninle paylaşamam.

0

Eğer arama yapmak için kullanıyor ve yalnızca yerel ayar için sıralama yapıyorsanız, senin işlevi basit gibi bir tablo kullanılarak karakter olanlardan başına bir bayt içine hem multi-byte dizeleri dönüştürmek işlevini yerine çağırmak için önermek A -> bir
à -> bir
á -> bir
ß -> ss
Ç -> c
ve böylece

üzerinde Sonra sadece strcmp arayıp sonuçları döndürür.

İlgili konular