Jak jest kodowane w const std :: wstring i jak zmienić na UTF-16
Utworzyłem ten minimalny działający przykładowy fragment kodu C ++, aby porównać bajty (według ich reprezentacji szesnastkowej) w a std::string
i a std::wstring
podczas definiowania ciągu z niemieckimi znakami spoza ASCII w obu typach.
#include <iostream>
#include <iomanip>
#include <string>
int main(int, char**) {
std::wstring wstr = L"äöüß";
std::string str = "äöüß";
for ( unsigned char c : str ) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
for ( wchar_t c : wstr ) {
std::cout << std::setw(4) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
return 0;
}
Dane wyjściowe tego fragmentu to
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Uruchomiłem to na komputerze PC z systemem Windows 10 64-bit Pro , kompilując z MSVC 2019 Community Edition w wersji 16.8.1, używając polecenia cmake systemu kompilacji z następującymiCMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(wstring VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
add_executable(wstring main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Czytałem, że std::string
są one oparte na char
typie, który jest jednobajtowy. Widzę, że dane wyjściowe mojego fragmentu kodu wskazują, że str
( std::string
zmienna) jest zakodowana w UTF-8 . Czytałem, że kompilatory Microsoftu używają wchar_t
s z 2 bajtami do tworzenia std::wstring
s (zamiast 4 bajtów wchar_t
przez np. GNU gcc) i dlatego oczekiwałbym, że wstr
( std::wstring
zmienna) będzie (dowolnego rodzaju) zakodowana w UTF-16 . Ale nie mogę zrozumieć, dlaczego „ß” (łacińskie ostre s) jest kodowane tak 0x00c30178
, jak się spodziewałem 0x00df
. Niech ktoś mi powie:
- Dlaczego tak się dzieje?
- Jak mogę skończyć z kodowaniem UTF-16
std::wstring
(Big Endian byłby w porządku, nie mam nic przeciwko BOM)? Czy prawdopodobnie muszę jakoś powiedzieć kompilatorowi? - Co to za kodowanie?
EDYCJA 1
zmieniony tytuł, ponieważ nie pasował poprawnie do pytań (a właściwie UTF-8 i UTF-16 to różne kodowania, więc ja sam sobie nową odpowiedź już ...)
EDYCJA 2
zapomniałem wspomnieć: używam amd64
celu wspomnianego kompilatora
EDYCJA 3
jeśli dodajesz /utf-8
flagę, jak wskazał w komentarzach dxiv (patrz jego połączony SO-Post ), otrzymuję żądane wyjście
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
który dla mnie wygląda jak UTF-16-BE (bez BOM). Ponieważ miałem problemy z poprawną kolejnością poleceń cmake, jest to mój bieżący CmakeLists.txt
plik. Ważne jest, aby umieścić add_compile_options
polecenie przed add_executable
poleceniem (dla wygody dodałem Powiadomienie)
cmake_minimum_required(VERSION 3.0.0)
project(enctest VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
if (MSVC)
message(NOTICE "compiling with MSVC")
add_compile_options(/utf-8)
endif()
add_executable(enctest main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Uważam, że if-endif
sposób jest bardziej czytelny niż generator-składnia, ale zamiast tego pisanie też by działało.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Uwaga: W przypadku projektów Qt istnieje fajny przełącznik dla .pro
pliku (zobacz ten post Qt-Form )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Wciąż pierwsza część mojego pytania jest otwarta: jakie jest kodowanie 0x00c30178
dla „ß” (łacińskie s)?
Odpowiedzi
Jak wyjaśniono w komentarzach, .cpp
plik źródłowy jest zakodowany w UTF-8. Bez BOM i bez jawnego /source-charset:utf-8
przełącznika kompilator Visual C ++ domyślnie przyjmuje, że plik źródłowy jest zapisywany w aktywnym kodowaniu strony kodowej. Z dokumentacji Set Source Character Set :
Domyślnie program Visual Studio wykrywa znacznik kolejności bajtów, aby określić, czy plik źródłowy jest w zakodowanym formacie Unicode, na przykład UTF-16 lub UTF-8. Jeśli nie zostanie znaleziony żaden znacznik kolejności bajtów, zakłada się, że plik źródłowy jest kodowany przy użyciu bieżącej strony kodowej użytkownika, chyba że określisz nazwę zestawu znaków lub stronę kodową za pomocą opcji / source-charset.
Kodowanie UTF-8 äöüß
to C3 A4 C3 B6 C3 BC C3 9F
, a więc wiersz:
std::wstring wstr = L"äöüß";
jest postrzegany przez kompilator jako:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Zakładając, że aktywną stroną kodową jest zwykły Windows-1252 , (rozszerzone) znaki są mapowane jako:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Dlatego L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"
jest tłumaczony na:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Aby uniknąć takiego (błędnego) tłumaczenia, Visual C ++ musi otrzymać informację, że plik źródłowy jest zakodowany jako UTF-8 przez przekazanie jawnego /source-charset:utf-8
(lub /utf-8) przełącznika kompilatora. W przypadku projektów opartych na CMake można to zrobić za pomocą, add_compile_options
jak pokazano w sekcji Możliwe, aby wymusić na CMake / MSVC użycie kodowania UTF-8 dla plików źródłowych bez BOM? C4819 .
w związku z tym oczekuje, że wstr (zmienna std :: wstring) będzie (dowolnego rodzaju) zakodowana w UTF-16
std::wstring
nie określa kodowania. Jest to sekwencja „szerokich znaków” dla pewnego rodzaju szerokich znaków (które są zdefiniowane w implementacji).
W bibliotece standardowej zdefiniowano aspekty konwersji do konwertowania do / z różnych kodowań.