Bagaimana cara const std :: wstring dikodekan dan cara mengubah ke UTF-16
Saya membuat cuplikan contoh C ++ kerja minimum ini untuk membandingkan byte (dengan representasi hex mereka) di a std::string
dan a std::wstring
ketika mendefinisikan string dengan karakter Jerman non-ASCII di kedua jenis.
#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;
}
Output dari potongan ini adalah
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Saya menjalankan ini di PC yang menjalankan Windows 10 64-bit Pro , mengkompilasi dengan MSVC 2019 Community Edition di Versi 16.8.1, menggunakan sistem build cmake dengan mengikutiCMakeLists.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)
Saya membaca, itu std::string
didasarkan pada char
jenis yang merupakan satu byte. Saya melihat bahwa output dari cuplikan saya menunjukkan bahwa str
( std::string
variabel) dikodekan UTF-8 . Saya membaca, bahwa kompiler Microsoft menggunakan wchar_t
s dengan 2 byte untuk membuat std::wstring
s (bukan 4 byte wchar_t
oleh misalnya GNU gcc) dan karena itu akan mengharapkan wstr
( std::wstring
variabel) menjadi (jenis apa pun) dikodekan UTF-16 . Tapi saya tidak tahu mengapa "ß" (latin sharp s) dikodekan seperti yang 0x00c30178
saya harapkan 0x00df
. Bisa tolong beri tahu saya:
- Mengapa ini terjadi?
- Bagaimana saya bisa berakhir dengan UTF-16 encoded
std::wstring
s (Big Endian akan baik-baik saja, saya tidak keberatan dengan BOM)? Apakah saya mungkin perlu memberi tahu kompiler entah bagaimana caranya? - Jenis pengkodean apa ini?
EDIT 1
mengubah judul, karena tidak sesuai dengan pertanyaan dengan benar (dan sebenarnya UTF-8 dan UTF-16 adalah pengkodean yang berbeda jadi saya sendiri yang baru jawabannya sudah ...)
EDIT 2
lupa menyebutkan: Saya menggunakan amd64
target dari kompilator yang disebutkan
EDIT 3
jika menambahkan /utf-8
bendera seperti yang ditunjukkan di komentar oleh dxiv (lihat SO-Post yang ditautkan ), saya mendapatkan output yang diinginkan
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
yang terlihat seperti UTF-16-BE (no BOM) untuk saya. Karena saya memiliki masalah dengan urutan perintah cmake yang benar, ini adalah CmakeLists.txt
file saya saat ini . Penting untuk meletakkan add_compile_options
perintah sebelum add_executable
perintah (saya menambahkan Pemberitahuan untuk kenyamanan)
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)
Saya menemukan if-endif
cara yang lebih mudah dibaca, daripada generator-sintaks, tapi menulis akan bekerja juga.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Catatan: Untuk Qt-Projects ada tombol yang bagus untuk .pro
file tersebut (lihat posting Qt-Form ini )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Masih bagian pertama dari pertanyaan saya terbuka: Apa pengkodean 0x00c30178
untuk "ß" (latin sharp s)?
Jawaban
Seperti yang dijelaskan di komentar, .cpp
file sumber dikodekan dengan UTF-8. Tanpa BOM, dan tanpa /source-charset:utf-8
switch eksplisit , compiler Visual C ++ secara default mengasumsikan file sumber disimpan dalam pengkodean halaman kode yang aktif. Dari dokumentasi Set Source Character Set :
Secara default, Visual Studio mendeteksi tanda urutan byte untuk menentukan apakah file sumber dalam format Unicode yang dikodekan, misalnya, UTF-16 atau UTF-8. Jika tanda urutan byte tidak ditemukan, ini mengasumsikan file sumber dikodekan menggunakan halaman kode pengguna saat ini, kecuali Anda menentukan nama kumpulan karakter atau halaman kode dengan menggunakan opsi / source-charset.
Pengkodean UTF-8 äöüß
adalah C3 A4 C3 B6 C3 BC C3 9F
, dan oleh karena itu baris:
std::wstring wstr = L"äöüß";
dilihat oleh kompilator sebagai:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Dengan asumsi codepage aktif menjadi Windows-1252 biasa , peta karakter (diperpanjang) sebagai:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Oleh karena itu L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"
diterjemahkan ke:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Untuk menghindari terjemahan (yang salah) seperti itu, Visual C ++ perlu diberi tahu bahwa file sumber dikodekan sebagai UTF-8 dengan meneruskan sakelar kompilator eksplisit /source-charset:utf-8
(atau /utf-8). Untuk proyek berbasis CMake, ini dapat dilakukan dengan menggunakan add_compile_options
seperti yang ditunjukkan di Kemungkinan memaksa CMake / MSVC menggunakan pengkodean UTF-8 untuk file sumber tanpa BOM? C4819 .
oleh karena itu akan mengharapkan wstr (variabel std :: wstring) menjadi (jenis apa pun) yang dikodekan UTF-16
std::wstring
tidak menentukan pengkodean. Ini adalah urutan "karakter lebar", untuk beberapa jenis karakter lebar (yang ditentukan implementasi).
Ada aspek konversi yang ditentukan di pustaka standar untuk mengonversi ke / dari penyandiaksaraan berbeda.