Jest tu kilka bardzo dobrych odpowiedzi, ale myślę, że mogę dodać kilka rzeczy dotyczących Windows / Visual Studio. To jest oparte na moich doświadczeniach z VS2015. W Linuksie w zasadzie odpowiedzią jest używanie std::string
wszędzie zakodowanych w UTF-8 . W systemie Windows / VS staje się bardziej złożony. Oto dlaczego. System Windows oczekuje, że ciągi przechowywane przy użyciu char
s zostaną zakodowane przy użyciu lokalnej strony kodowej. Jest to prawie zawsze zestaw znaków ASCII, po którym następuje 128 innych znaków specjalnych, w zależności od lokalizacji. Pozwolę sobie tylko stwierdzić, że nie tylko przy korzystaniu z Windows API, istnieją trzy inne główne miejsca, w których te ciągi wchodzą w interakcje ze standardowym C ++. Są to literały łańcuchowe, dane wyjściowe do std::cout
używania <<
i przekazywania nazwy pliku std::fstream
.
Będę tutaj z góry, że jestem programistą, a nie specjalistą od języków. Rozumiem, że USC2 i UTF-16 nie są takie same, ale dla moich celów są wystarczająco blisko, aby były wymienne i używam ich jako takich tutaj. Nie jestem pewien, którego systemu Windows używa, ale generalnie nie muszę też wiedzieć. W tej odpowiedzi podałem UCS2, więc z góry przepraszam, jeśli zdenerwowałem kogoś swoją niewiedzą w tej sprawie i cieszę się, że mogę go zmienić, jeśli coś jest nie tak.
Literały łańcuchowe
Jeśli wpiszesz literały łańcuchowe zawierające tylko znaki, które mogą być reprezentowane przez twoją stronę kodową, VS zapisze je w twoim pliku z 1 bajtem na kodowanie znaków na podstawie twojej strony kodowej. Zauważ, że jeśli zmienisz stronę kodową lub przekażesz swoje źródło innemu programistowi, używając innej strony kodowej, to myślę (ale nie przetestowałem), że znak skończy się inaczej. Jeśli uruchomisz kod na komputerze przy użyciu innej strony kodowej, nie jestem pewien, czy znak również się zmieni.
Jeśli wpiszesz literały ciągów, które nie mogą być reprezentowane przez twoją stronę kodową, VS poprosi cię o zapisanie pliku jako Unicode. Plik zostanie następnie zakodowany jako UTF-8. Oznacza to, że wszystkie znaki spoza ASCII (w tym te, które znajdują się na stronie kodowej) będą reprezentowane przez 2 lub więcej bajtów. Oznacza to, że jeśli podasz swoje źródło komuś innemu, źródło będzie wyglądać tak samo. Jednak przed przekazaniem źródła do kompilatora VS konwertuje tekst zakodowany w UTF-8 na tekst zakodowany na stronie kodowej, a wszelkie znaki brakujące na stronie kodowej są zastępowane przez?
.
Jedynym sposobem, aby zagwarantować prawidłowe odwzorowanie literału ciągów Unicode w VS, jest poprzedzenie literału ciągów literą L
szeroką. W takim przypadku VS skonwertuje tekst zakodowany w UTF-8 z pliku na UCS2. Następnie musisz przekazać dosłowny ciąg znaków do std::wstring
konstruktora lub przekonwertować go na utf-8 i umieścić w pliku std::string
. Lub jeśli chcesz, możesz użyć funkcji Windows API do zakodowania go za pomocą strony kodowej, aby umieścić go w std::string
, ale równie dobrze możesz nie użyć szerokiego ciągu literałów.
std :: cout
Podczas wysyłania do konsoli za pomocą <<
możesz używać tylko std::string
, std::wstring
a nie, a tekst musi być zakodowany przy użyciu lokalnej strony kodowej. Jeśli tak std::wstring
, musisz go przekonwertować za pomocą jednej z funkcji Windows API, a wszelkie znaki spoza twojej strony kodowej zostaną zastąpione przez ?
(być może możesz zmienić znak, nie pamiętam).
std :: nazwy plików fstream
System operacyjny Windows używa UCS2 / UTF-16 dla swoich nazw plików, więc bez względu na stronę kodową możesz mieć pliki o dowolnym znaku Unicode. Oznacza to jednak, że aby uzyskać dostęp do plików ze znakami spoza strony kodowej lub tworzyć je, musisz ich użyć std::wstring
. Nie ma innego wyjścia. Jest to rozszerzenie specyficzne dla Microsoft, std::fstream
więc prawdopodobnie nie będzie się kompilowało w innych systemach. Jeśli używasz std :: string, możesz używać tylko nazw plików zawierających tylko znaki na stronie kodowej.
Twoje opcje
Jeśli pracujesz tylko w systemie Linux, prawdopodobnie nie zaszedłeś tak daleko. Po prostu użyj UTF-8 std::string
wszędzie.
Jeśli pracujesz tylko w systemie Windows, użyj UCS2 std::wstring
wszędzie. Niektórzy puriści mogą powiedzieć, że używają UTF8, a następnie konwertują w razie potrzeby, ale po co zawracać sobie głowę kłopotami.
Jeśli jesteś wieloplatformowy, to szczerze mówiąc, to bałagan. Jeśli próbujesz używać UTF-8 wszędzie w systemie Windows, musisz być bardzo ostrożny z literałami ciągów i przesyłaniem ich do konsoli. Możesz łatwo zepsuć tam swoje łańcuchy. Jeśli używasz std::wstring
wszędzie w systemie Linux, możesz nie mieć dostępu do szerokiej wersji std::fstream
, więc musisz wykonać konwersję, ale nie ma ryzyka uszkodzenia. Więc osobiście uważam, że jest to lepsza opcja. Wielu by się nie zgodziło, ale nie jestem sam - jest to ścieżka podana na przykład przez wxWidgets.
Inną opcją może być wpisanieef unicodestring
jak std::string
w Linuksie i std::wstring
Windowsie i posiadanie makra o nazwie UNI (), które ma prefiks L w Windows i nic w Linuksie, a następnie kod
#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>
#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
std::string result;
//Call WideCharToMultiByte to do the conversion
return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
return str;
}
#endif
int main()
{
unicodestring fileName(UNI("fileName"));
std::ofstream fout;
fout.open(fileName);
std::cout << formatForConsole(fileName) << std::endl;
return 0;
}
myślę, że byłoby dobrze na każdej platformie.
Odpowiedzi
Aby odpowiedzieć na twoje pytania
1) Jeśli programujesz dla systemu Windows, to cały czas, jeśli masz wiele platform, to może cały czas, chyba że chcesz poradzić sobie z możliwymi problemami z korupcją w systemie Windows lub napisać kod z konkretną platformą #ifdefs
aby obejść różnice, jeśli tylko używasz Linux wtedy nigdy.
2) Tak. Ponadto w systemie Linux możesz używać go również do wszystkich znaków Unicode. W systemie Windows możesz go używać tylko dla wszystkich kodów Unicode, jeśli wybierzesz ręczne kodowanie przy użyciu UTF-8. Ale interfejs API systemu Windows i standardowe klasy C ++ będą oczekiwaćstd::string
kodowania przy użyciu lokalnej strony kodowej. Obejmuje to wszystkie znaki ASCII oraz kolejne 128 znaków, które zmieniają się w zależności od strony kodowej, z której komputer ma korzystać.
3) Uważam, że tak, ale jeśli nie, to jest to po prostu zwykła czcionka „std :: basic_string” używająca wchar_t
zamiastchar
4) Szeroki znak to typ znaku, który jest większy niż standardowy 1-bajtowy char
typ. W systemie Windows jest to 2 bajty, w systemie Linux - 4 bajty.