Apa yang “salah” dengan C ++ wchar_t dan wstrings? Apa sajakah alternatif untuk karakter lebar?

87

Saya telah melihat banyak orang di komunitas C ++ (terutama ## c ++ di freenode) membenci penggunaan wstringsdan wchar_t, dan penggunaannya di api windows. Apa sebenarnya yang "salah" dengan wchar_tdan wstring, dan jika saya ingin mendukung internasionalisasi, apa sajakah alternatif untuk karakter yang luas?

Ken Li
sumber
1
Punya referensi untuk itu?
Dani
14
Mungkin utas yang luar biasa ini akan menjawab semua pertanyaan Anda? stackoverflow.com/questions/402283/stdwstring-vs-stdstring
MrFox
15
Di Windows, Anda tidak punya pilihan. API internalnya dirancang untuk UCS-2, yang masuk akal pada saat itu karena sebelum pengkodean UTF-8 dan UTF-16 dengan panjang variabel distandarisasi. Tetapi sekarang setelah mereka mendukung UTF-16, mereka berakhir dengan yang terburuk dari kedua dunia.
jamesdlin
12
utf8everywhere.org memiliki diskusi yang bagus tentang alasan untuk menghindari karakter yang luas.
JoeG
5
@jamesdlin Tentu Anda punya pilihan. Library nowide menyediakan cara mudah untuk mengonversi string hanya saat meneruskan ke API. Panggilan API dengan string biasanya berfrekuensi rendah, jadi cara yang masuk akal adalah dengan mengonversi ad-hok dan memiliki file serta variabel internal di UTF-8 sepanjang waktu.
Pavel Radzivilovsky

Jawaban:

115

Apa itu wchar_t?

wchar_t didefinisikan sedemikian rupa sehingga pengkodean char lokal apa pun dapat dikonversi ke representasi wchar_t di mana setiap wchar_t mewakili tepat satu titik kode:

Jenis wchar_t adalah jenis berbeda yang nilainya dapat mewakili kode berbeda untuk semua anggota rangkaian karakter tambahan terbesar yang ditentukan di antara lokal yang didukung (22.3.1).

                                                                               - C ++ [basic.fundamental] 3.9.1 / 5

Ini tidak mengharuskan wchar_t cukup besar untuk mewakili karakter apa pun dari semua lokal secara bersamaan. Artinya, encoding yang digunakan untuk wchar_t mungkin berbeda di antara bahasa lokal. Yang berarti Anda tidak bisa serta merta mengonversi string menjadi wchar_t menggunakan satu lokal dan kemudian mengonversi kembali ke char menggunakan lokal lain. 1

Karena menggunakan wchar_t sebagai representasi umum antara semua lokal tampaknya menjadi penggunaan utama untuk wchar_t dalam praktiknya, Anda mungkin bertanya-tanya apa gunanya jika bukan itu.

Maksud dan tujuan asli wchar_t adalah untuk membuat pemrosesan teks sederhana dengan mendefinisikannya sedemikian rupa sehingga memerlukan pemetaan satu-ke-satu dari unit kode string ke karakter teks, sehingga memungkinkan penggunaan algoritme sederhana yang sama seperti yang digunakan dengan string ascii untuk bekerja dengan bahasa lain.

Sayangnya kata-kata spesifikasi wchar_t mengasumsikan pemetaan satu-ke-satu antara karakter dan titik kode untuk mencapai ini. Unicode mematahkan asumsi 2 itu , jadi Anda juga tidak dapat menggunakan wchar_t dengan aman untuk algoritme teks sederhana.

Ini berarti perangkat lunak portabel tidak dapat menggunakan wchar_t baik sebagai representasi umum untuk teks antar lokal, atau untuk mengaktifkan penggunaan algoritme teks sederhana.

Apa gunanya wchar_t hari ini?

Tidak banyak, untuk kode portabel. Jika __STDC_ISO_10646__ditentukan maka nilai wchar_t secara langsung mewakili titik kode Unicode dengan nilai yang sama di semua lokal. Itu membuatnya aman untuk melakukan konversi antar-lokal yang disebutkan sebelumnya. Namun Anda tidak dapat hanya mengandalkannya untuk memutuskan bahwa Anda dapat menggunakan wchar_t dengan cara ini karena, sementara sebagian besar platform unix mendefinisikannya, Windows tidak meskipun Windows menggunakan lokal wchar_t yang sama di semua lokal.

Alasan Windows tidak menentukan __STDC_ISO_10646__adalah karena Windows menggunakan UTF-16 sebagai pengkodean wchar_t, dan karena UTF-16 menggunakan pasangan pengganti untuk merepresentasikan titik kode yang lebih besar dari U + FFFF, yang berarti UTF-16 tidak memenuhi persyaratan untuk __STDC_ISO_10646__.

Untuk kode platform tertentu wchar_t mungkin lebih berguna. Ini pada dasarnya diperlukan di Windows (misalnya, beberapa file tidak dapat dibuka tanpa menggunakan nama file wchar_t), meskipun Windows adalah satu-satunya platform di mana ini benar sejauh yang saya tahu (jadi mungkin kita dapat menganggap wchar_t sebagai 'Windows_char_t').

Melihat ke belakang, wchar_t jelas tidak berguna untuk menyederhanakan penanganan teks, atau sebagai penyimpanan untuk teks independen lokal. Kode portabel tidak boleh mencoba menggunakannya untuk tujuan ini. Kode non-portabel mungkin merasa berguna hanya karena beberapa API memerlukannya.

Alternatif

Alternatif yang saya suka adalah menggunakan string C berenkode UTF-8, bahkan pada platform yang tidak terlalu bersahabat dengan UTF-8.

Dengan cara ini seseorang dapat menulis kode portabel menggunakan representasi teks umum di seluruh platform, menggunakan tipe data standar untuk tujuan yang dimaksudkan, mendapatkan dukungan bahasa untuk jenis tersebut (misalnya string literal, meskipun beberapa trik diperlukan untuk membuatnya berfungsi untuk beberapa kompiler), beberapa dukungan pustaka standar, dukungan debugger (lebih banyak trik mungkin diperlukan), dll. Dengan karakter yang lebar, umumnya lebih sulit atau tidak mungkin untuk mendapatkan semua ini, dan Anda mungkin mendapatkan bagian yang berbeda pada platform yang berbeda.

Satu hal yang tidak disediakan UTF-8 adalah kemampuan untuk menggunakan algoritme teks sederhana seperti yang mungkin dilakukan dengan ASCII. Dalam UTF-8 ini tidak lebih buruk dari pengkodean Unicode lainnya. Sebenarnya ini mungkin dianggap lebih baik karena representasi unit multi-kode dalam UTF-8 lebih umum dan oleh karena itu bug dalam penanganan kode seperti representasi karakter dengan lebar variabel lebih cenderung diperhatikan dan diperbaiki daripada jika Anda mencoba untuk tetap menggunakan UTF -32 dengan NFC atau NFKC.

Banyak platform menggunakan UTF-8 sebagai pengkodean karakter asli mereka dan banyak program tidak memerlukan pemrosesan teks yang signifikan, sehingga menulis program internasionalisasi pada platform tersebut sedikit berbeda dengan menulis kode tanpa mempertimbangkan internasionalisasi. Menulis kode portabel yang lebih luas, atau menulis di platform lain memerlukan penyisipan konversi di batas API yang menggunakan pengkodean lain.

Alternatif lain yang digunakan oleh beberapa perangkat lunak adalah memilih representasi lintas platform, seperti larik pendek unsigned yang menyimpan data UTF-16, dan kemudian menyediakan semua dukungan perpustakaan dan hanya menanggung biaya dalam dukungan bahasa, dll.

C ++ 11 menambahkan jenis karakter lebar baru sebagai alternatif untuk wchar_t, char16_t dan char32_t dengan fitur bahasa / pustaka yang menyertai. Ini sebenarnya tidak dijamin sebagai UTF-16 dan UTF-32, tetapi saya tidak membayangkan implementasi besar apa pun akan menggunakan hal lain. C ++ 11 juga meningkatkan dukungan UTF-8, misalnya dengan literal string UTF-8 sehingga tidak perlu mengelabui VC ++ agar menghasilkan string yang dikodekan UTF-8 (meskipun saya dapat terus melakukannya daripada menggunakan u8awalan) .

Alternatif untuk dihindari

TCHAR: TCHAR adalah untuk memigrasi program Windows kuno yang mengasumsikan pengkodean lama dari char ke wchar_t, dan paling baik dilupakan kecuali jika program Anda ditulis pada milenium sebelumnya. Ini tidak portabel dan secara inheren tidak spesifik tentang pengkodeannya dan bahkan tipe datanya, membuatnya tidak dapat digunakan dengan API berbasis non-TCHAR. Karena tujuannya adalah migrasi ke wchar_t, yang telah kita lihat di atas bukanlah ide yang baik, tidak ada nilai apa pun dalam menggunakan TCHAR.


1. Karakter yang dapat direpresentasikan dalam string wchar_t tetapi tidak didukung di lokasi mana pun tidak perlu diwakili dengan satu nilai wchar_t. Ini berarti bahwa wchar_t dapat menggunakan pengkodean lebar variabel untuk karakter tertentu, pelanggaran lain yang jelas dari maksud wchar_t. Meskipun dapat diperdebatkan bahwa karakter yang dapat direpresentasikan oleh wchar_t sudah cukup untuk mengatakan bahwa lokal 'mendukung' karakter itu, dalam hal ini pengkodean lebar-variabel tidak legal dan penggunaan UTF-16 oleh Window tidak sesuai.

2. Unicode memungkinkan banyak karakter untuk direpresentasikan dengan beberapa titik kode, yang menciptakan masalah yang sama untuk algoritme teks sederhana seperti pengkodean lebar variabel. Bahkan jika seseorang secara ketat mempertahankan normalisasi yang tersusun, beberapa karakter masih memerlukan banyak titik kode. Lihat: http://www.unicode.org/standard/where/

bames53
sumber
3
Tambahan: utf8everywhere.org merekomendasikan penggunaan UTF-8 di Windows, dan Boost.Nowide dijadwalkan untuk tinjauan formal.
Yakov Galka
2
Hal terbaik, tentu saja, adalah menggunakan C # atau VB.Net di Windows :) Atau C / Win32 lama biasa. Tetapi jika Anda harus menggunakan C ++, maka TCHAR adalah cara terbaik untuk melakukannya. Yang defaultnya ke "wchar_t" di MSVS2005 dan lebih tinggi. IMHO ...
paulsm4
4
@BrendanMcK: Tentu, kode yang menggunakan Win32 API di windows dan API lain di sistem lain tidak ada. Baik? Masalah dengan pendekatan microsoft ("gunakan wchar secara internal di mana pun di aplikasi Anda") adalah yang memengaruhi bahkan kode yang tidak menghubungkan sistem secara langsung dan dapat portabel.
Yakov Galka
4
Masalahnya adalah Anda harus menggunakan fungsi khusus Windows karena keputusan Microsoft untuk tidak mendukung UTF-8 karena halaman kode ANSI "merusak" Pustaka C (++) Standar. Misalnya, Anda tidak bisa fopenfile yang namanya berisi karakter non-ANSI.
dan04
11
@ dan04 Ya, Anda tidak dapat menggunakan pustaka standar pada Windows, tetapi Anda dapat membuat antarmuka portabel yang membungkus pustaka standar pada platform lain dan mengonversi dari UTF-8 ke wchar_t secara langsung sebelum menggunakan fungsi Win32 W.
bames53
20

Tidak ada yang "salah" dengan wchar_t. Masalahnya adalah, pada NT 3.x hari, Microsoft memutuskan bahwa Unicode adalah Bagus (itu), dan menerapkan Unicode sebagai 16-bit, karakter wchar_t. Jadi kebanyakan literatur Microsoft dari pertengahan 90-an cukup banyak menyamakan Unicode == utf16 == wchar_t.

Yang, sayangnya, sama sekali tidak demikian. "Karakter lebar" tidak harus 2 byte, di semua platform, dalam semua keadaan.

Ini adalah salah satu primer terbaik di "Unicode" (terlepas dari pertanyaan ini, terlepas dari C ++) yang pernah saya lihat: Saya sangat merekomendasikannya:

Dan sejujurnya saya percaya bahwa cara terbaik untuk menangani "8-bit ASCII" vs "karakter lebar Win32" vs "wchar_t-in-general" adalah dengan menerima bahwa "Windows Berbeda" ... dan buat kode yang sesuai.

MENURUT OPINI SAYA...

PS:

Saya setuju dengan jamesdlin di atas:

Di Windows, Anda tidak punya pilihan. API internalnya dirancang untuk UCS-2, yang masuk akal pada saat itu karena sebelum pengkodean UTF-8 dan UTF-16 dengan panjang variabel distandarisasi. Tetapi sekarang setelah mereka mendukung UTF-16, mereka berakhir dengan yang terburuk dari kedua dunia.

paulsm4
sumber