2010-11-12 10 views
11

W końcu wróciłem do mojego głównego zadania - przeniesienia raczej dużego projektu C++ z Windowsa na Maca.Wieloplatformowe łańcuchy znaków (i Unicode) w C++

Od razu uderzył mnie problem, w którym wchar_t ma 16 bitów w systemie Windows, ale 32 bitów na Macu. Jest to problem, ponieważ wszystkie łańcuchy są reprezentowane przez wchar_t, a tam będą przesyłane dane stringów pomiędzy maszynami Windows i Mac (zarówno w danych na dysku, jak iw sieciowych formularzach danych). Ze względu na sposób, w jaki działa, nie byłoby łatwo przekształcić ciągi w jakiś popularny format przed wysłaniem i odebraniem danych.

Naprawdę zaczęliśmy ostatnio obsługiwać znacznie więcej języków, dlatego zaczynamy zajmować się wieloma danymi Unicode (jak również obsługą języków pisanych od prawej do lewej).

Mogę teraz kombinować wiele pomysłów i powodować więcej problemów dla siebie niż potrzebnych, dlatego właśnie zadaję to pytanie. Uważamy, że przechowywanie wszystkich danych w łańcuchu w pamięci jako UTF-8 ma wiele sensu. Rozwiązuje problem wchar_t będący różnym rozmiarem, co oznacza, że ​​możemy w łatwy sposób obsługiwać wiele języków, a także dramatycznie zmniejsza nasz ślad pamięci (mamy mnóstwo - głównie angielskich - napisów) załadowanych) - ale nie wygląda na to, że wiele osób robi to. Czy jest coś, czego nam brakuje? Istnieje oczywisty problem, z którym musisz sobie poradzić, gdzie długość łańcucha może być mniejsza niż rozmiar pamięci przechowującej dane ciągu.

Lub używa UTF-16 lepszym pomysłem? Czy powinniśmy trzymać się wchar_t i napisać kod, aby przekonwertować między wchar_t i, powiedzmy, Unicode w miejscach, w których odczytujemy/zapisujemy na dysku lub w sieci?

Zdaję sobie sprawę, że jest to niebezpiecznie bliskie pytania o opinie - ale denerwujemy się, że przeoczyliśmy coś oczywistego, ponieważ nie wydaje się, że istnieje wiele klas ciągów Unicode (na przykład) - ale jest jeszcze mnóstwo kod do konwersji do/z Unicode jak w boost :: locale, iconv, utf-cpp i ICU.

+0

Tylko jedno słowo powiedzieć. http://utf8everywhere.org –

Odpowiedz

7

Zawsze używaj protokołu zdefiniowanego dla bajtu, gdy jest zaangażowany plik lub połączenie sieciowe. Nie należy polegać na tym, jak kompilator C++ przechowuje cokolwiek w pamięci. W przypadku tekstu Unicode oznacza to wybór zarówno kolejności kodowania, jak i kolejności bajtów (w porządku, UTF-8 nie dba o kolejność bajtów). Nawet jeśli platformy, które obecnie chcesz obsługiwać, mają podobne architektury, prawdopodobnie pojawi się inna popularna platforma z innym zachowaniem lub nawet nowy system dla jednej z twoich istniejących platform, a będziesz zadowolony, że napisałeś przenośny kod.

1

Zgodnie z ogólną zasadą: UTF-16 do przetwarzania, UTF-8 do komunikacji &.

Oczywiście, każda zasada może zostać złamana, a ta nie jest wyryta w kamieniu. Ale musisz wiedzieć, kiedy można go przerwać.

Na przykład dobrym pomysłem może być użycie czegoś innego, jeśli środowisko, którego używasz, potrzebuje czegoś innego. Jednak interfejsy API systemu Mac OS X używają formatu UTF-16, takiego samego jak system Windows. Tak więc UTF-16 ma więcej sensu. Łatwiej jest konwertować, zanim umieścisz/uzyskasz rzeczy w sieci (ponieważ prawdopodobnie robisz to w 2-3 procedurach) niż robi się wszystkie konwersje, aby wywołać interfejsy API systemu operacyjnego.

Ważne jest również rodzaj aplikacji, którą tworzysz. Jeśli jest to coś z bardzo małym przetwarzaniem tekstu i bardzo małymi wezwaniami do systemu (coś takiego jak serwer e-mail, który głównie przenosi obiekty bez ich zmiany), UTF-8 może być dobrym wyborem.

Więc tak bardzo, jak możesz nienawidzić tej odpowiedzi, "to zależy".

2

Zwykle używam UTF-8 jako wewnętrznej reprezentacji. Tracisz tylko sprawdzanie długości łańcucha, z czego tak naprawdę nie jest przydatne. W przypadku konwersji interfejsu API systemu Windows używam własnych funkcji konwersji Win32 I devised here. Ponieważ Mac i Linux to (dla większość z nich to ze standardowym UTF-8, nie ma potrzeby konwertowania czegokolwiek). Darmowe bonusy:

  1. użyć zwykłego starego std::string.
  2. Transport w sieci/strumieniu bajtów.
  3. Dla większości języków, ładny ślad pamięci.
  4. Dodatkowe funkcjonalności: utf8cpp
+3

UTF-8 nie ** nie ** pozwala na użycie "plain old' std :: string' ". Być może, jeśli wszystko, co chcesz zrobić, to przechowywać łańcuch, który jest w porządku, ale nie możesz zmodyfikować łańcucha w tej formie bez pisania własnego pliku UTF-8, jeśli używasz tego kontenera. (nie możesz używać funkcji składowych takich jak 'std :: string :: find' i oczekiwać, że będą działać poprawnie z ciągami UTF-8) Zbyt wielu ludzi myśli" Och, po prostu użyję UTF-8 "i myślę, że mogą po prostu dalej traktuj wszystko jak tablice znaków, co jest fałszywe. –

+5

@Billy: To prawda w przypadku kodowania wielobajtowego. std :: string jest kontenerem znaków, a nie glifów i idealnie nadaje się do zakodowania tekstu zakodowanego w UTF-8 w std :: string i przetwarzania go z czymś takim jak utf8cpp –

+2

@Nemanja: Tak, dobrze jest używać std :: ciąg do przechowywania, ale możesz technicznie * przechowywać * wszystko w std :: string (o ile możesz podać dla niego fikcyjny aspekt 'std :: char_traits'). Jednak kiedy powiesz "Możesz użyć zwykłego starego std :: string", ludzie będą zakładać, że mogą używać tej klasy do czegokolwiek innego niż przechowywanie danych. Jeśli ** just storage ** jest tym, czego szukasz, powinieneś raczej użyć 'vector'. –

0

ICU ma C++ klasy String, UnicodeString

+1

ICU to fajna biblioteka do tego typu rzeczy. Niestety jest również ** ogromny ** (skompilowany rozmiar ICU to około 25 MB). W niektórych przypadkach może to być w porządku, ale w innych jest (oczywiście) nie w porządku. Niektóre osoby faktycznie nie potrzebują wszystkich funkcji, które zapewnia. OTOH, każdy, kto implementuje to, co robi, zwykle robi to źle (rzeczy takie jak sortowanie różnią się w zależności od ustawień narodowych, a ICU poprawnie to obsługuje). –

+0

Wiele z tych danych to dane dla 500 lokalizacji i setek konwerterów oraz wszystkich możliwych bibliotek. Można go łatwo dostosowywać z punktu widzenia danych i kodu, jeśli nie potrzebujesz wszystkiego. Na przykład podstawowa biblioteka ICuuc zawiera około 1,4 MB danych bez danych. –

Powiązane problemy