2009-01-12 9 views
9

Patrząc na unicode standard, zaleca się stosowanie zwykłych char s do przechowywania łańcuchów zakodowanych w UTF-8. Czy działa to zgodnie z oczekiwaniami w C++ i podstawowym std::string, czy też istnieją przypadki, w których kodowanie UTF-8 może powodować problemy?Jaki jest najlepszy sposób przechowywania ciągów UTF-8 w pamięci w C/C++?

Na przykład przy obliczaniu długości może nie być tożsama z liczbą bajtów - jak to ma być obsługiwane? Czytając standard, prawdopodobnie używam macierzy do przechowywania, ale nadal będę musiał napisać funkcje takie jak strlen itd., Które działają na zakodowanym tekście, ponieważ o ile rozumiem problem, standard rutyny są albo tylko ASCII, albo oczekują szerokich literałów (16 bitów lub więcej), które nie są zalecane przez standard Unicode. Do tej pory najlepszym źródłem znalazłem o rzeczy kodowania jest post na Joel's on Software, ale to nie wyjaśnia, co my biedni C++ developer powinien używać :)

Odpowiedz

5

Istnieje biblioteka o nazwie "UTF8-CPP", która umożliwia przechowywanie ciągów znaków UTF-8 w standardowych obiektach std :: string oraz zapewnia dodatkowe funkcje do wyliczania i manipulowania znakami utf-8.

Nie testowałem jeszcze tego, więc nie wiem, ile to jest warte, ale rozważam jego samodzielne użycie.

+0

To jest prawdopodobnie droga. Istnieje również biblioteka ICU, która zajmuje mniej więcej to samo. – sastanin

0

Od UTF-8 and Unicode FAQ: C support for Unicode:

#include <stdio.h> 
#include <locale.h> 

int main() 
{ 
    if (!setlocale(LC_CTYPE, "")) { 
    fprintf(stderr, "Can't set the specified locale! " 
      "Check LANG, LC_CTYPE, LC_ALL.\n"); 
    return 1; 
    } 
    printf("%ls\n", L"Schöne Grüße"); 
    return 0; 
} 

również z here:

dobrą wiadomością jest to, że jeśli używasz wchar_t* sznurki i rodzinę funkcje związane z nimi, takie jak wprintf, wcslen i wcslcat, jesteś zajmujący się wartościami Unicode. W świecie C++ można użyć interfejsu std::wstring do , aby zapewnić przyjazny interfejs. Mój jedyny zarzut dotyczy tego, że są to 32-bitowe znaki (4 bajtów), więc są one pamięcią świnie dla wszystkich języków. Powodem tego jest to, że każdy z możliwych znaków może być reprezentowany przez jedną wartość: .

PS. Jest to prawdopodobnie związane z Linuksem. Jest biblioteka ICU do obsługi skomplikowanych rzeczy.

+0

To nie działa poprawnie, gdy próbuję go na OS X z GCC 4.01: Drukuje znaki spoza ASCII jako znaki ewakuowane w kodzie ósemkowym. Kiedy piszę printf ("% s \ n", "Schöne Grüße"); zamiast tego drukuje poprawnie. W związku z tym nie jest to rozwiązanie do uzyskania liczby znaków utf-8 w ciągu znaków. –

+0

Nie mogę powiedzieć dla OS X, ale ten przykład zdecydowanie działa z GCC 4.3.2 na GNU/Linux, * w locale UTF-8 *. Jakie są twoje ustawienia regionalne w OS X? Podejrzewam, że nie jest to lokalizacja w standardzie Unicode. Ponadto, prawdopodobnie ustawienia regionalne są obsługiwane inaczej w OS X, nie wiem. – sastanin

+1

Źle na tylu poziomach, obawiam się. Chars poza gwarantowanym zestawem znaków; zakładając, że konsola może wydrukować wchar_t. wchar_t to 2 bajty na większości komputerów, – MSalters

1

Na czym się zdecydowaliśmy: przechowuj UTF8 w std :: string. Możesz teraz wykonywać większość operacji, z wyjątkiem takich czynności jak obliczanie długości. Użyj funkcji konwersji UTF8-> std :: wstring (boost :: from_utf8, na przykład), aby przekonwertować na std :: wstring, gdy potrzebujesz takich operacji.

2

Zależy od tego, co chcesz zrobić z ciągiem znaków UTF8. Jeśli wszystko, co cię interesuje, to czytanie i wysyłanie ciągów UTF8, to wszystko działa tak długo, jak ustawiłeś prawidłowe ustawienia narodowe. Zrobiliśmy to przez jakiś czas. Mamy kilka procesów serwera, które nic nie robią z łańcuchami jako takimi. Tam ciągi są ustawiane przez użytkownika w Javie i docierają jako UTF8, a my obsługujemy je w standardowych buforach. Następnie przesyłamy dane z powrotem do Javy, która je konwertuje.

Jeśli chcesz mieć długość w znakach UTF8, potrzebujesz funkcji, które mogą obsłużyć tłumaczenie.

Ale można toczyć własne np utf8-strlen

2

strlen zlicza liczbę znaków niż null przed pierwszym \ 0. W UTF-8 ta liczba to liczba przy zdrowych zmysłach (liczba użytych bajtów), ale liczba ta nie jest liczbą znaków (jeden znak UTF-8 to typowo 1-4 znaków). basic_string nie przechowuje \ 0, ale również utrzymuje liczbę bajtów.

strcpy lub basic_string copy ctor skopiuj wszystkie bajty bez szukania zbyt blisko.

Wyszukiwanie podciągu działa OK, ze względu na sposób kodowania UTF_8. Dopuszczalne wartości dla pierwszego bajtu znaku różnią się od drugiego do czwartego bajtu (pierwszy z nich nigdy nie zaczyna się od 10xxxxxx, zawsze ostatni)

Podejmowanie podciągu jest trudne - w jaki sposób określasz pozycję? Jeśli początek i koniec zostały znalezione przez wyszukanie znaczników tekstowych ASCII (np. [I]), to nie ma problemu. Po prostu otrzymasz bajty w środku, które również są poprawnym łańcuchem UTF8. Nie można jednak harcować pozycji, ani nawet względnych przesunięć. Nawet relatywne przesunięcie +1 postaci może być trudne; ile to jest bajtów? Kończysz pisanie funkcji, takiej jak SkipOneChar.

3

Przykładem z ICU library (C, C++, Java):

#include <iostream> 
#include <unicode/unistr.h> // using ICU library 

int main(int argc, char *argv[]) { 
    // constructing a Unicode string 
    UnicodeString ustr1("Привет"); // using platform's default codepage 
    // calculating the length in characters, should be 6 
    int ulen1=ustr1.length(); 
    // extracting encoded characters from a string 
    int const bufsize=25; 
    char encoded[bufsize]; 
    ustr1.extract(0,ulen1,encoded,bufsize,"UTF-8"); // forced UTF-8 encoding 
    // printing the result 
    std::cout << "Length of " << encoded << " is " << ulen1 << "\n"; 
    return 0; 
} 

budynek jak

$ g++ -licuuc -o icu-example{,.cc} 

działa

$ ./icu-example 
Length of Привет is 6 

u mnie działa na Linux GCC 4.3.2 i libicu 3.8.1. Należy pamiętać, że drukuje on w formacie UTF-8, niezależnie od ustawień regionalnych systemu. Nie zobaczysz go poprawnie, jeśli nie jest to UTF-8.

Powiązane problemy