Seberapa baik pustaka standar C ++ mendukung unicode?
Sangat.
Pemindaian cepat melalui fasilitas perpustakaan yang mungkin menyediakan dukungan Unicode memberi saya daftar ini:
- Perpustakaan string
- Perpustakaan lokalisasi
- Pustaka input / output
- Pustaka ekspresi reguler
Saya pikir semua kecuali yang pertama memberikan dukungan yang mengerikan. Saya akan kembali ke hal itu dengan lebih detail setelah memutar cepat melalui pertanyaan Anda yang lain.
Apakahstd::string
melakukan apa yang seharusnya?
Iya. Menurut standar C ++, inilah yang std::string
harus dilakukan dan saudara-saudaranya:
Templat kelas basic_string
menjelaskan objek yang dapat menyimpan urutan yang terdiri dari sejumlah objek char-arbitrary dengan elemen pertama dari urutan di posisi nol.
Nah, std::string
apakah itu baik-baik saja. Apakah itu menyediakan fungsionalitas khusus Unicode? Tidak.
Haruskah itu Mungkin tidak. std::string
baik-baik saja sebagai urutan char
objek. Itu berguna; satu-satunya gangguan adalah bahwa itu adalah tampilan yang sangat rendah dari teks dan standar C ++ tidak memberikan yang lebih tinggi.
Bagaimana saya menggunakannya?
Gunakan sebagai urutan char
objek; berpura-pura itu adalah sesuatu yang lain pasti berakhir dengan rasa sakit.
Di mana ada potensi masalah?
Seluruh tempat? Ayo lihat...
Perpustakaan string
Perpustakaan string memberi kita basic_string
, yang hanya merupakan urutan dari apa yang disebut standar "objek seperti char". Saya menyebutnya unit kode. Jika Anda menginginkan tampilan teks tingkat tinggi, ini bukan yang Anda cari. Ini adalah tampilan teks yang cocok untuk serialisasi / deserialisasi / penyimpanan.
Ini juga menyediakan beberapa alat dari pustaka C yang dapat digunakan untuk menjembatani kesenjangan antara dunia sempit dan dunia Unicode: c16rtomb
/ mbrtoc16
dan c32rtomb
/ mbrtoc32
.
Perpustakaan lokalisasi
Perpustakaan lokalisasi masih percaya bahwa salah satu dari "objek char-like" sama dengan satu "karakter". Ini tentu saja konyol, dan membuatnya mustahil untuk mendapatkan banyak hal berfungsi dengan baik di luar beberapa subset kecil Unicode seperti ASCII.
Pertimbangkan, misalnya, apa yang panggilan standar "antarmuka kenyamanan" di <locale>
header:
template <class charT> bool isspace (charT c, const locale& loc);
template <class charT> bool isprint (charT c, const locale& loc);
template <class charT> bool iscntrl (charT c, const locale& loc);
// ...
template <class charT> charT toupper(charT c, const locale& loc);
template <class charT> charT tolower(charT c, const locale& loc);
// ...
Bagaimana Anda mengharapkan salah satu dari fungsi-fungsi ini untuk mengkategorikan dengan benar, misalnya, U + 1F34C ʙᴀɴᴀɴᴀ, seperti pada u8"🍌"
atau u8"\U0001F34C"
? Tidak mungkin itu akan berhasil, karena fungsi-fungsi itu hanya mengambil satu unit kode sebagai input.
Ini bisa berfungsi dengan lokal yang sesuai jika Anda char32_t
hanya menggunakan :U'\U0001F34C'
adalah unit kode tunggal di UTF-32.
Namun, itu tetap berarti Anda hanya mendapatkan transformasi casing sederhana dengan toupper
dan tolower
, yang, misalnya, tidak cukup baik untuk beberapa lokal Jerman: "ß" huruf besar menjadi "SS" ☦ tetapi toupper
hanya dapat mengembalikan satu karakter unit kode .
Selanjutnya, wstring_convert
/ wbuffer_convert
dan aspek konversi kode standar.
wstring_convert
digunakan untuk mengkonversi antara string dalam satu pengkodean yang diberikan menjadi string dalam pengkodean lain yang diberikan. Ada dua tipe string yang terlibat dalam transformasi ini, yang standar memanggil string byte dan string lebar. Karena istilah-istilah ini benar-benar menyesatkan, saya masing-masing lebih suka menggunakan "serial" dan "deserialized", †.
Pengkodean untuk mengkonversi antara diputuskan oleh codecvt (segi konversi kode) diteruskan sebagai argumen tipe templat ke wstring_convert
.
wbuffer_convert
melakukan fungsi yang serupa tetapi sebagai buffer aliran deserialized lebar yang membungkus byte stream buffer serial. Setiap I / O dilakukan melalui buffer aliran serial byte yang mendasari dengan konversi ke dan dari pengkodean yang diberikan oleh argumen codecvt. Menulis serialisasi ke buffer itu, dan kemudian menulis darinya, dan membaca membaca ke buffer dan kemudian deserializes dari itu.
Standar menyediakan beberapa template kelas codecvt untuk digunakan dengan fasilitas ini: codecvt_utf8
, codecvt_utf16
, codecvt_utf8_utf16
, dan beberapa codecvt
spesialisasi. Bersama-sama, aspek standar ini menyediakan semua konversi berikut. (Catatan: dalam daftar berikut, pengkodean di sebelah kiri selalu berupa string berseri / streambuf, dan pengodean di sebelah kanan selalu berupa string / streambuf deserialisasi; standar memungkinkan konversi di kedua arah).
- UTF-8 ↔ UCS-2 dengan
codecvt_utf8<char16_t>
, dan di codecvt_utf8<wchar_t>
mana sizeof(wchar_t) == 2
;
- UTF-8 ↔ UTF-32 dengan
codecvt_utf8<char32_t>
, codecvt<char32_t, char, mbstate_t>
, dan codecvt_utf8<wchar_t>
di mana sizeof(wchar_t) == 4
;
- UTF-16 ↔ UCS-2 dengan
codecvt_utf16<char16_t>
, dan di codecvt_utf16<wchar_t>
mana sizeof(wchar_t) == 2
;
- UTF-16 ↔ UTF-32 dengan
codecvt_utf16<char32_t>
, dan di codecvt_utf16<wchar_t>
mana sizeof(wchar_t) == 4
;
- UTF-8 ↔ UTF-16 dengan
codecvt_utf8_utf16<char16_t>
, codecvt<char16_t, char, mbstate_t>
, dan codecvt_utf8_utf16<wchar_t>
di manasizeof(wchar_t) == 2
;
- sempit ↔ lebar dengan
codecvt<wchar_t, char_t, mbstate_t>
- no-op dengan
codecvt<char, char, mbstate_t>
.
Beberapa di antaranya bermanfaat, tetapi ada banyak hal aneh di sini.
Yang pertama — ibu pengganti yang suci! skema penamaan itu berantakan.
Lalu, ada banyak dukungan UCS-2. UCS-2 adalah penyandian dari Unicode 1.0 yang digantikan pada tahun 1996 karena hanya mendukung bidang multibahasa dasar. Mengapa panitia berpikir ingin fokus pada pengkodean yang digantikan lebih dari 20 tahun yang lalu, saya tidak tahu ‡. Ini tidak seperti dukungan untuk lebih banyak pengkodean buruk atau apa pun, tetapi UCS-2 muncul terlalu sering di sini.
Saya akan mengatakan bahwa char16_t
ini jelas dimaksudkan untuk menyimpan unit kode UTF-16. Namun, ini adalah salah satu bagian dari standar yang berpikir sebaliknya. codecvt_utf8<char16_t>
tidak ada hubungannya dengan UTF-16. Sebagai contoh, wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")
akan dikompilasi dengan baik, tetapi akan gagal tanpa syarat: input akan diperlakukan sebagai string UCS-2u"\xD83C\xDF4C"
, yang tidak dapat dikonversi ke UTF-8 karena UTF-8 tidak dapat menyandikan nilai apa pun dalam kisaran 0xD800-0xDFFF.
Masih di depan UCS-2, tidak ada cara untuk membaca dari aliran UTF-16 byte menjadi string UTF-16 dengan sisi-sisi ini. Jika Anda memiliki urutan UTF-16 byte, Anda tidak dapat membatalkan deserialize menjadi string char16_t
. Ini mengejutkan, karena ini lebih atau kurang merupakan konversi identitas. Yang lebih mengejutkan adalah kenyataan bahwa ada dukungan untuk deserialisasi dari aliran UTF-16 ke dalam string UCS-2 dengancodecvt_utf16<char16_t>
, yang sebenarnya merupakan konversi yang hilang.
Dukungan UTF-16-as-bytes cukup bagus, meskipun: mendukung mendeteksi endianess dari BOM, atau memilihnya secara eksplisit dalam kode. Ini juga mendukung menghasilkan output dengan dan tanpa BOM.
Ada beberapa kemungkinan konversi yang lebih menarik. Tidak ada cara deserialize dari aliran UTF-16 byte atau string ke string UTF-8, karena UTF-8 tidak pernah didukung sebagai bentuk deserialized.
Dan di sini dunia sempit / lebar benar-benar terpisah dari dunia UTF / UCS. Tidak ada konversi antara pengkodean sempit / lebar gaya lama dan pengkodean Unicode apa pun.
Pustaka input / output
Perpustakaan I / O dapat digunakan untuk membaca dan menulis teks dalam pengkodean Unicode menggunakan wstring_convert
dan wbuffer_convert
fasilitas yang dijelaskan di atas. Saya tidak berpikir ada banyak hal lain yang perlu didukung oleh bagian dari perpustakaan standar ini.
Pustaka ekspresi reguler
Saya telah menjelaskan masalah dengan C ++ regexes dan Unicode di Stack Overflow sebelumnya. Saya tidak akan mengulangi semua poin tersebut di sini, tetapi hanya menyatakan bahwa C ++ regex tidak memiliki dukungan Unicode level 1, yang merupakan jumlah minimum untuk membuatnya dapat digunakan tanpa menggunakan UTF-32 di mana-mana.
Itu dia?
Ya itu saja. Itulah fungsionalitas yang ada. Ada banyak fungsi Unicode yang tidak terlihat seperti normalisasi atau algoritma segmentasi teks.
U + 1F4A9 . Apakah ada cara untuk mendapatkan dukungan Unicode yang lebih baik di C ++?
Tersangka yang biasa: ICU dan Boost.Locale .
String String byte adalah, tidak mengherankan, string byte, yaitu char
objek. Namun, tidak seperti string string literal , yang selalu merupakan array wchar_t
objek, "string lebar" dalam konteks ini tidak harus berupa string wchar_t
objek. Faktanya, standar tidak pernah secara eksplisit mendefinisikan apa arti "string lebar", jadi kita tinggal menebak arti dari penggunaan. Karena terminologi standarnya ceroboh dan membingungkan, saya menggunakan istilah saya sendiri, atas nama kejelasan.
Pengkodean seperti UTF-16 dapat disimpan sebagai urutan char16_t
, yang kemudian tidak memiliki endianness; atau mereka dapat disimpan sebagai urutan byte, yang memiliki endianness (setiap pasangan byte berturut-turut dapat mewakili char16_t
nilai yang berbeda tergantung pada endianness). Standar ini mendukung kedua bentuk ini. Urutan char16_t
lebih berguna untuk manipulasi internal dalam program. Urutan byte adalah cara untuk bertukar string seperti itu dengan dunia eksternal. Istilah yang akan saya gunakan daripada "byte" dan "lebar" dengan demikian "serial" dan "deserialized".
‡ Jika Anda akan mengatakan "tetapi Windows!" pegang 🐎🐎 Anda . Semua versi Windows sejak Windows 2000 menggunakan UTF-16.
☦ Ya, saya tahu tentang großes Eszett (ẞ), tetapi bahkan jika Anda mengubah semua bahasa Jerman semalam menjadi ß huruf besar menjadi ẞ, masih ada banyak kasus lain di mana ini akan gagal. Coba gunakan huruf besar U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. Tidak ada ʟᴀᴛɪɴ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ; hanya naik menjadi dua Fs. Atau U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; tidak ada modal yang dikompilasi sebelumnya; hanya naik menjadi huruf J besar dan huruf menggabungkan.