Bagaimana cara const std :: wstring dikodekan dan cara mengubah ke UTF-16

Nov 30 2020

Saya membuat cuplikan contoh C ++ kerja minimum ini untuk membandingkan byte (dengan representasi hex mereka) di a std::stringdan a std::wstringketika 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::stringdidasarkan pada charjenis yang merupakan satu byte. Saya melihat bahwa output dari cuplikan saya menunjukkan bahwa str( std::stringvariabel) dikodekan UTF-8 . Saya membaca, bahwa kompiler Microsoft menggunakan wchar_ts dengan 2 byte untuk membuat std::wstrings (bukan 4 byte wchar_toleh misalnya GNU gcc) dan karena itu akan mengharapkan wstr( std::wstringvariabel) menjadi (jenis apa pun) dikodekan UTF-16 . Tapi saya tidak tahu mengapa "ß" (latin sharp s) dikodekan seperti yang 0x00c30178saya harapkan 0x00df. Bisa tolong beri tahu saya:

  • Mengapa ini terjadi?
  • Bagaimana saya bisa berakhir dengan UTF-16 encoded std::wstrings (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 amd64target dari kompilator yang disebutkan

EDIT 3

jika menambahkan /utf-8bendera 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.txtfile saya saat ini . Penting untuk meletakkan add_compile_optionsperintah sebelum add_executableperintah (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-endifcara 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 .profile tersebut (lihat posting Qt-Form ini )

win32 {
    QMAKE_CXXFLAGS += /utf-8
}

Masih bagian pertama dari pertanyaan saya terbuka: Apa pengkodean 0x00c30178untuk "ß" (latin sharp s)?

Jawaban

5 dxiv Dec 01 2020 at 16:41

Seperti yang dijelaskan di komentar, .cppfile sumber dikodekan dengan UTF-8. Tanpa BOM, dan tanpa /source-charset:utf-8switch 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_optionsseperti yang ditunjukkan di Kemungkinan memaksa CMake / MSVC menggunakan pengkodean UTF-8 untuk file sumber tanpa BOM? C4819 .

MarshallClow Nov 30 2020 at 20:50

oleh karena itu akan mengharapkan wstr (variabel std :: wstring) menjadi (jenis apa pun) yang dikodekan UTF-16

std::wstringtidak 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.