2008-11-11 17 views
5

Jak przekonwertować ciąg znaków Unicode na ciąg znaków utf-8 lub utf-16? Mój projekt VS2005 jest przy użyciu zestawu Unicode char, natomiast dostarczyć sqlite w cppJak przekonwertować ciąg znaków Unicode na ciąg znaków utf-8 lub utf-16?

int sqlite3_open(
    const char *filename, /* Database filename (UTF-8) */ 
    sqlite3 **ppDb   /* OUT: SQLite db handle */ 
); 
int sqlite3_open16(
    const void *filename, /* Database filename (UTF-16) */ 
    sqlite3 **ppDb   /* OUT: SQLite db handle */ 
); 

do otwierania folderu. Jak mogę przekonwertować ciąg, CString lub Wstring na zestaw znaków UTF-8 lub UTF-16?

Dziękuję bardzo!

Odpowiedz

6

Krótka odpowiedź:

Nie wymagana, jeśli używasz ciągów Unicode, takie jak CString lub wstring konwersji. Użyj sqlite3_open16(). Będziesz musiał upewnić się, że przekazałeś wskaźnik WCHAR (odlany do void *. Wydaje się być lame! Nawet jeśli ta lib jest platformą crossową, myślę, że mogliby zdefiniować szeroki typ znaku, który zależy od platformy i jest mniej nieprzyjazny niż void *) do API. Takie jak na CString: (void*)(LPCWSTR)strFilename

Dłuższa odpowiedź:

Nie masz ciąg Unicode, który chcesz przekonwertować na UTF-8 lub UTF16. Masz swój ciąg znaków Unicode reprezentowany w twoim programie przy użyciu danego kodowania: Unicode nie jest reprezentacją binarną jako taką. Kodowanie mówi, w jaki sposób punkty kodowe Unicode (wartości numeryczne) są reprezentowane w pamięci (układ binarny liczby). UTF8 i UTF16 są najczęściej używanymi kodowaniami. Są jednak bardzo różne.

Gdy projekt VS mówi "Zestaw znaków Unicode", oznacza to, że "znaki są zakodowane jako UTF16". Dlatego możesz użyć sqlite3_open16() bezpośrednio. Konwersja nie jest wymagana. Znaki są przechowywane w typie WCHAR (w przeciwieństwie do char), który zajmuje 16 bitów (Fallback na standardowym typie C wchar_t, który zajmuje 16 bitów na Win32.Może być inny na innych platformach.Dzięki poprawce, Warcaby).

Jest jeszcze jeden szczegół, na który warto zwrócić uwagę: UTF16 występuje w 2 smakach: Big Endian i Little Endian. To jest uporządkowanie bajtowe tych 16 bitów. Prototyp funkcji, który podajesz dla UTF16, nie mówi, które zamawianie jest używane. Ale jesteś całkiem bezpieczny zakładając, że sqlite używa tej samej endian-ności co Windows (Little Endian IIRC, znam kolejność, ale zawsze miałem problem z nazwami :-)).

EDIT: Odpowiedź na komentarz przez Warcaby:

UTF16 wykorzystuje 16 bitów Kod Jednostki. Pod Win32 (i tylko na Win32), wchar_t jest używany do takiej jednostki pamięci. Sztuczka polega na tym, że niektóre znaki Unicode wymagają sekwencji 2 takich 16-bitowych jednostek kodu. Nazywane są parami zastępczymi.

W taki sam sposób UTF8 reprezentuje 1 znak przy użyciu sekwencji od 1 do 4 bajtów. Jednak UTF8 są używane z typem char.

+3

Nie, nie, nie! sqlite3_open16() używa argumentu 'void *', ponieważ jest określony jako UTF16, * NOT * wchar_t, który ma różną wielkość na różnych platformach i może lub nie może być UTF16 (tj. glibc ma 4-bajtowy wchar_t). –

+0

Checkers: zobacz moją odpowiedź jako EDIT tutaj powyżej –

+1

Tak, jestem świadomy reprezentacji UTF16. Nie można jednak zakładać, że wewnętrzna reprezentacja wchar_t jest taka sama na wszystkich platformach, tak nie jest. –

7

Użyj funkcji WideCharToMultiByte. Podaj CP_UTF8 dla parametru CodePage.

CHAR buf[256]; // or whatever 
WideCharToMultiByte(
    CP_UTF8, 
    0, 
    StringToConvert, // the string you have 
    -1, // length of the string - set -1 to indicate it is null terminated 
    buf, // output 
    __countof(buf), // size of the buffer in bytes - if you leave it zero the return value is the length required for the output buffer 
    NULL,  
    NULL 
); 

Również domyślne kodowanie dla aplikacji Unicode w oknach jest UTF-16LE, więc może nie trzeba wykonywać żadnych tłumaczenie i po prostu korzystać z drugą wersję sqlite3_open16.

+0

Nie polecam stałego bufora; zamiast tego użyj dynamicznie przydzielonego bufora (np. std :: vector), rozwijając w razie potrzeby (gdy WideCharToMultiByte powie ci, że twój ciąg jest za mały). –

+1

Muszę się nie zgodzić: Pokazujesz, jak konwertować z UTF16 na UTF8. Nie jest to wymagane przez OP, ponieważ wydaje się, że dostępna jest funkcja dla szerokich łańcuchów znaków: sqlite3_open16(). IMO, poprawna odpowiedź to: użyj sqlite3_open16(). –

+0

@Chris, dlatego właśnie powiedziałem "lub cokolwiek" i umieściłem komentarz na temat rozmiaru bufora wyjściowego - nie chciałem zbytnio komplikować sprawy –

0

UTF-8 i utf-16 to oba kodowane znaki "Unicode". Prawdopodobnie mówisz o utf-32, który jest kodowaniem znaków o stałym rozmiarze. Może szukając

"Convert utf-32 into utf-8 or utf-16"

zapewnia pewne rezultaty lub inne dokumenty na ten temat.

3

Wszystkie typy ciągów C++ są neutralne dla zestawu znaków. Po prostu ustawiają szerokość znaku i nie przyjmują dalszych założeń. Wstring używa 16-bitowych znaków w Windows, odpowiadających w przybliżeniu utf-16, ale nadal zależy od tego, co przechowujesz w wątku. Wstring w żaden sposób nie wymusza, że ​​dane, które w nim umieścisz, muszą być poprawne utf16. Windows używa utf16, gdy zdefiniowana jest UNICODE, więc najprawdopodobniej twoje struny są już utf16 i nie musisz nic robić.

Kilka innych osób zasugerowało użycie funkcji WideCharToMultiByte, która jest (jedną z) ścieżek do przejścia z utf16 na utf8. Ale ponieważ sqlite może obsługiwać utf16, nie powinno to być konieczne.

0

Najprostszym sposobem na to jest użycie CStringA. Klasa CString jest typedef dla CStringA (wersja ASCII) lub CStringW (szeroka wersja char). Obie te klasy mają konstruktory do konwersji typów łańcuchów. Zwykle używam:

sqlite3_open(CStringA(L"MyWideCharFileName"), ...);