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::stringharus dilakukan dan saudara-saudaranya:
Templat kelas basic_stringmenjelaskan objek yang dapat menyimpan urutan yang terdiri dari sejumlah objek char-arbitrary dengan elemen pertama dari urutan di posisi nol.
Nah, std::stringapakah itu baik-baik saja. Apakah itu menyediakan fungsionalitas khusus Unicode? Tidak.
Haruskah itu Mungkin tidak. std::stringbaik-baik saja sebagai urutan charobjek. 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 charobjek; 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/ mbrtoc16dan 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_thanya menggunakan :U'\U0001F34C' adalah unit kode tunggal di UTF-32.
Namun, itu tetap berarti Anda hanya mendapatkan transformasi casing sederhana dengan toupperdan tolower, yang, misalnya, tidak cukup baik untuk beberapa lokal Jerman: "ß" huruf besar menjadi "SS" ☦ tetapi toupperhanya dapat mengembalikan satu karakter unit kode .
Selanjutnya, wstring_convert/ wbuffer_convertdan aspek konversi kode standar.
wstring_convertdigunakan 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_convertmelakukan 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 codecvtspesialisasi. 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_tini 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_convertdan wbuffer_convertfasilitas 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 charobjek. Namun, tidak seperti string string literal , yang selalu merupakan array wchar_tobjek, "string lebar" dalam konteks ini tidak harus berupa string wchar_tobjek. 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_tnilai yang berbeda tergantung pada endianness). Standar ini mendukung kedua bentuk ini. Urutan char16_tlebih 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.