std :: wstring VS std :: string

742

Saya tidak dapat memahami perbedaan antara std::stringdan std::wstring. Saya tahu wstringmendukung karakter lebar seperti karakter Unicode. Saya sudah mendapat pertanyaan berikut:

  1. Ketika saya harus menggunakan std::wstringlebih std::string?
  2. Bisakah std::stringmemegang seluruh rangkaian karakter ASCII, termasuk karakter khusus?
  3. Apakah std::wstringdidukung oleh semua kompiler C ++ yang populer?
  4. Apa sebenarnya " karakter lebar "?
Rapptz
sumber
10
Set ASCII charachter tidak memiliki banyak karakter "spesial", yang paling eksotis mungkin `(backquote). std :: string dapat menampung sekitar 0,025% dari semua karakter Unicode (biasanya, karakter 8 bit)
MSalters
3
Informasi bagus tentang karakter lebar dan jenis apa yang digunakan dapat ditemukan di sini: programmers.stackexchange.com/questions/102205/…
Yariv
14
Yah, dan sejak kita di tahun 2012, utf8everywhere.org ditulis. Cukup banyak menjawab semua pertanyaan tentang hak dan kesalahan dengan C ++ / Windows.
Pavel Radzivilovsky
42
@MSalters: std :: string dapat menampung 100% dari semua karakter Unicode, bahkan jika CHAR_BIT adalah 8. Itu tergantung pada pengkodean std :: string, yang mungkin UTF-8 pada tingkat sistem (seperti hampir di semua tempat kecuali untuk windows ) atau pada level aplikasi Anda. Pengkodean bawaan asli tidak mendukung Unicode? Tidak masalah, hanya jangan menggunakannya, gunakan UTF-8 saja.
Yakov Galka
8
Bacaan hebat tentang topik ini: utf8everywhere.org
Timothy Shields

Jawaban:

993

string? wstring?

std::stringadalah basic_stringtemplated pada a char, dan std::wstringpada a wchar_t.

char vs. wchar_t

charseharusnya memiliki karakter, biasanya karakter 8-bit.
wchar_tseharusnya memiliki karakter yang luas, dan kemudian, hal menjadi rumit:
Di Linux, a wchar_tadalah 4 byte, sedangkan pada Windows, itu 2 byte.

Bagaimana dengan Unicode ?

Masalahnya adalah tidak satu charpun wchar_tsecara langsung terkait dengan unicode.

Di Linux?

Mari kita mengambil OS Linux: Sistem Ubuntu saya sudah sadar unicode. Ketika saya bekerja dengan string char, itu secara asli dikodekan dalam UTF-8 (yaitu string Unicode karakter). Kode berikut:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

menampilkan teks berikut:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

Anda akan melihat teks "olé" di charbenar-benar dibangun oleh empat karakter: 110, 108, 195 dan 169 (tidak termasuk nol di belakangnya). (Saya akan membiarkan Anda mempelajari wchar_tkode sebagai latihan)

Jadi, ketika bekerja dengan charLinux, Anda biasanya harus menggunakan Unicode tanpa menyadarinya. Dan seperti std::stringbekerja dengan char, jadi std::stringsudah siap-unicode.

Perhatikan bahwa std::string, seperti API string C, akan menganggap string "olé" memiliki 4 karakter, bukan tiga. Jadi Anda harus berhati-hati saat memotong / bermain dengan karakter unicode karena beberapa kombinasi karakter dilarang di UTF-8.

Di Windows?

Di Windows, ini sedikit berbeda. Win32 harus mendukung banyak aplikasi yang bekerja dengan chardan pada berbagai charset / codepages yang diproduksi di seluruh dunia, sebelum munculnya Unicode.

Jadi solusi mereka sangat menarik: Jika sebuah aplikasi bekerja char, maka string char dikodekan / dicetak / ditampilkan pada label GUI menggunakan charset / codepage lokal pada mesin. Misalnya, "olé" akan menjadi "olé" di Windows yang dilokalisasikan di Prancis, tetapi akan menjadi sesuatu yang berbeda pada Windows yang dilokalisasi dengan cyrillic ("olй" jika Anda menggunakan Windows-1251 ). Dengan demikian, "aplikasi historis" biasanya akan tetap bekerja dengan cara lama yang sama.

Untuk aplikasi berbasis Unicode, Windows menggunakan wchar_t, yang lebar 2-byte, dan dikodekan dalam UTF-16 , yang Unicode dikodekan pada karakter 2-byte (atau paling tidak, UCS-2 yang paling kompatibel, yang hampir merupakan hal yang sama IIRC).

Aplikasi yang menggunakan chardikatakan "multibyte" (karena setiap mesin terbang terdiri dari satu atau lebih chars), sedangkan aplikasi yang menggunakan wchar_tdikatakan "widechar" (karena masing-masing mesin terbang terdiri dari satu atau dua wchar_t. Lihat MultiByteToWideChar dan WideCharToMultiByte Win32 API konversi untuk info lebih lanjut.

Jadi, jika Anda bekerja di Windows, Anda sangat ingin menggunakan wchar_t(kecuali jika Anda menggunakan kerangka kerja yang menyembunyikan itu, seperti GTK + atau QT ...). Faktanya adalah bahwa di balik layar, Windows bekerja dengan wchar_tstring, sehingga bahkan aplikasi historis akan memiliki charstring mereka dikonversi wchar_tketika menggunakan API seperti SetWindowText()(fungsi API tingkat rendah untuk mengatur label pada Win32 GUI).

Masalah memori?

UTF-32 adalah 4 byte per karakter, jadi tidak ada banyak yang harus ditambahkan, jika saja teks UTF-8 dan teks UTF-16 akan selalu menggunakan lebih sedikit atau jumlah memori yang sama dari teks UTF-32 (dan biasanya kurang ).

Jika ada masalah memori, maka Anda harus tahu daripada kebanyakan bahasa barat, teks UTF-8 akan menggunakan memori lebih sedikit daripada UTF-16 yang sama.

Namun, untuk bahasa lain (Cina, Jepang, dll.), Memori yang digunakan akan sama, atau sedikit lebih besar untuk UTF-8 daripada untuk UTF-16.

Secara keseluruhan, UTF-16 sebagian besar akan menggunakan 2 dan kadang-kadang 4 byte per karakter (kecuali jika Anda berurusan dengan beberapa jenis mesin terbang bahasa esoterik (Klingon? Peri?), Sedangkan UTF-8 akan menghabiskan 1 hingga 4 byte.

Lihat http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 untuk info lebih lanjut.

Kesimpulan

  1. Kapan saya harus menggunakan std :: wstring over std :: string?

    Di Linux? Hampir tidak pernah (§).
    Di Windows? Hampir selalu (§).
    Pada kode lintas platform? Tergantung pada perangkat Anda ...

    (§): kecuali Anda menggunakan toolkit / kerangka kerja yang mengatakan sebaliknya

  2. Bisakah std::stringmemegang semua set karakter ASCII termasuk karakter khusus?

    Perhatikan: A std::stringcocok untuk memegang buffer 'biner', di mana a std::wstringtidak!

    Di Linux? Iya.
    Di Windows? Hanya karakter khusus yang tersedia untuk lokal saat ini dari pengguna Windows.

    Sunting (Setelah komentar dari Johann Gerell ):
    a std::stringakan cukup untuk menangani semua charstring berbasis (masing char- masing berupa angka dari 0 hingga 255). Tapi:

    1. ASCII seharusnya berubah dari 0 menjadi 127. Semakin tinggi charBUKAN ASCII.
    2. a chardari 0 hingga 127 akan disimpan dengan benar
    3. a chardari 128 hingga 255 akan memiliki signifikasi tergantung pada pengkodean Anda (unicode, non-unicode, dll.), tetapi itu akan dapat menampung semua mesin terbang Unicode selama kode tersebut dikodekan dalam UTF-8.
  3. Apakah std::wstringdidukung oleh hampir semua kompiler C ++ yang populer?

    Sebagian besar, dengan pengecualian kompiler berbasis GCC yang porting ke Windows.
    Ini bekerja pada g ++ 4.3.2 saya (di Linux), dan saya menggunakan Unicode API di Win32 sejak Visual C ++ 6.

  4. Apa sebenarnya karakter yang lebar?

    Pada C / C ++, ini adalah tipe karakter yang ditulis wchar_tyang lebih besar dari chartipe karakter sederhana . Seharusnya digunakan untuk memasukkan karakter di dalam yang indeksnya (seperti mesin terbang Unicode) lebih besar dari 255 (atau 127, tergantung ...).

paercebal
sumber
4
@gnud: Mungkin wchar_t seharusnya cukup untuk menangani semua karakter UCS-2 (sebagian besar karakter UTF-16) sebelum munculnya UTF-16 ... Atau mungkin Microsoft memang memiliki prioritas selain POSIX, seperti memberikan akses mudah ke Unicode tanpa memodifikasi penggunaan kode char pada Win32.
paercebal
4
@ Korin Sbarnea: UTF-8 dapat mengambil 1-6 byte, tetapi tampaknya standar membatasi 1-4. Lihat en.wikipedia.org/wiki/UTF8#Deskripsi untuk informasi lebih lanjut.
paercebal
8
Sementara contoh-contoh ini menghasilkan hasil yang berbeda di Linux dan Windows, program C ++ berisi perilaku yang didefinisikan implementasi apakah olèdikodekan sebagai UTF-8 atau tidak. Selanjutnya, alasan Anda tidak bisa native streaming wchar_t *untuk std::coutini karena jenis tidak sesuai sehingga program sakit-dibentuk dan tidak ada hubungannya dengan penggunaan pengkodean. Perlu ditunjukkan bahwa apakah Anda menggunakan std::stringatau std::wstringbergantung pada preferensi penyandian Anda sendiri daripada platform, terutama jika Anda ingin kode Anda menjadi portabel.
John Leidegren
14
Windows sebenarnya menggunakan UTF-16 dan sudah cukup lama, versi Windows yang lebih lama memang menggunakan UCS-2 tetapi ini tidak lagi berlaku. Satu-satunya masalah saya di sini adalah kesimpulan yang std::wstringharus digunakan pada Windows karena lebih cocok untuk Unicode Windows API yang saya pikir salah. Jika satu-satunya kekhawatiran Anda adalah memanggil ke dalam Unicode Windows API dan tidak membuat string maka yakin tapi saya tidak membeli ini sebagai kasus umum.
John Leidegren
15
@ John Leidegren If your only concern was calling into the Unicode Windows API and not marshalling strings then sure:: Lalu, kami setuju. Saya mengkode dalam C ++, bukan JavaScript. Menghindari marshalling yang tidak berguna atau proses lain yang berpotensi memakan biaya saat runtime ketika hal itu dapat dilakukan pada waktu kompilasi adalah inti dari bahasa itu. Pengkodean terhadap WinAPI dan menggunakan std::stringhanyalah sumber daya runtime yang tidak dapat dibenarkan. Anda merasa salah, dan tidak apa-apa, karena itu adalah sudut pandang Anda. Saya sendiri tidak akan menulis kode dengan pesimisasi di Windows hanya karena terlihat lebih baik dari sisi Linux.
paercebal
71

Saya sarankan menghindari std::wstringpada Windows atau di tempat lain, kecuali bila diperlukan oleh antarmuka, atau di mana saja di dekat panggilan Windows API dan konversi encoding masing-masing sebagai gula sintaksis.

Pandangan saya dirangkum dalam http://utf8everywhere.org di mana saya adalah rekan penulis.

Kecuali jika aplikasi Anda adalah API-call-centric, mis. Terutama aplikasi UI, sarannya adalah untuk menyimpan string Unicode di std :: string dan disandikan dalam UTF-8, melakukan konversi dekat panggilan API. Manfaat yang diuraikan dalam artikel lebih penting daripada gangguan konversi, terutama dalam aplikasi yang kompleks. Ini sangat ganda untuk pengembangan multi-platform dan perpustakaan.

Dan sekarang, menjawab pertanyaan Anda:

  1. Beberapa alasan lemah. Itu ada karena alasan historis, di mana widechars diyakini sebagai cara yang tepat untuk mendukung Unicode. Sekarang digunakan untuk antarmuka API yang lebih suka string UTF-16. Saya menggunakannya hanya di sekitar panggilan API semacam itu.
  2. Ini tidak ada hubungannya dengan std :: string. Itu bisa menampung pengodean apa pun yang Anda masukkan ke dalamnya. Satu-satunya pertanyaan adalah bagaimana Anda memperlakukan kontennya. Rekomendasi saya adalah UTF-8, sehingga akan dapat menampung semua karakter Unicode dengan benar. Ini adalah praktik umum di Linux, tetapi saya pikir program Windows juga harus melakukannya.
  3. Tidak.
  4. Karakter lebar adalah nama yang membingungkan. Pada hari-hari awal Unicode, ada kepercayaan bahwa karakter dapat dikodekan dalam dua byte, karenanya namanya. Hari ini, singkatan dari "setiap bagian dari karakter yang panjangnya dua byte". UTF-16 dilihat sebagai urutan pasangan byte tersebut (alias Wide karakter). Karakter dalam UTF-16 membutuhkan satu atau dua pasangan.
Pavel Radzivilovsky
sumber
37

Jadi, setiap pembaca di sini sekarang harus memiliki pemahaman yang jelas tentang fakta, situasi. Jika tidak, maka Anda harus membaca jawaban komprehensif luar biasa paercebal [btw: terima kasih!].

Kesimpulan pragmatis saya sangat sederhana: semua yang "+ karakter" pengkodean C ++ (dan STL) pada dasarnya rusak dan tidak berguna. Salahkan pada Microsoft atau tidak, itu tidak akan membantu.

Solusi saya, setelah investigasi mendalam, banyak frustrasi dan pengalaman konsekuensial adalah sebagai berikut:

  1. menerima, bahwa Anda harus bertanggung jawab sendiri atas pengodean dan konversi (dan Anda akan melihat bahwa sebagian besar darinya agak sepele)

  2. gunakan std :: string untuk string yang dikodekan UTF-8 (hanya a typedef std::string UTF8String)

  3. menerima bahwa objek UTF8String seperti itu hanya sebuah wadah bodoh, tapi murah. Jangan pernah mengakses dan / atau memanipulasi karakter di dalamnya secara langsung (tidak ada pencarian, ganti, dan sebagainya). Anda bisa, tetapi Anda benar-benar hanya sangat, benar-benar tidak ingin membuang waktu Anda menulis algoritma manipulasi teks untuk string multi-byte! Bahkan jika orang lain sudah melakukan hal bodoh seperti itu, jangan lakukan itu! Biarlah! (Yah, ada skenario di mana masuk akal ... cukup gunakan perpustakaan ICU untuk itu).

  4. gunakan std :: wstring untuk string yang disandikan UCS-2 ( typedef std::wstring UCS2String) - ini adalah kompromi, dan konsesi untuk kekacauan yang diperkenalkan oleh API WIN32). UCS-2 sudah cukup untuk sebagian besar dari kita (lebih lanjut tentang itu nanti ...).

  5. gunakan instance UCS2String setiap kali diperlukan akses karakter per karakter (baca, manipulasi, dan sebagainya). Pemrosesan berbasis karakter apa pun harus dilakukan dalam representasi NON-multibyte. Sederhana, cepat, mudah.

  6. tambahkan dua fungsi utilitas untuk mengkonversi bolak-balik antara UTF-8 dan UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

Konversi ini mudah, google harus membantu di sini ...

Itu dia. Gunakan UTF8String di mana pun memori berharga dan untuk semua I / O UTF-8. Gunakan UCS2String di mana pun string harus diuraikan dan / atau dimanipulasi. Anda dapat mengonversi antara dua representasi tersebut kapan saja.

Alternatif & Perbaikan

  • konversi dari & ke pengkodean karakter bita tunggal (misalnya ISO-8859-1) dapat diwujudkan dengan bantuan tabel terjemahan biasa, mis. const wchar_t tt_iso88951[256] = {0,1,2,...};dan kode yang sesuai untuk konversi ke & dari UCS2.

  • jika UCS-2 tidak mencukupi, daripada beralih ke UCS-4 ( typedef std::basic_string<uint32_t> UCS2String)

ICU atau perpustakaan unicode lainnya?

Untuk barang-barang canggih.

Frunsi
sumber
Sial, tidak baik untuk mengetahui bahwa dukungan Unicode asli tidak ada.
Mihai Danila
@ Frunsi, saya ingin tahu apakah Anda sudah mencoba Glib :: ustring dan jika demikian, apa pendapat Anda?
Caroline Beltran
@CarolineBeltran: Saya tahu Glib, tapi saya tidak pernah menggunakannya, dan saya mungkin tidak akan pernah menggunakannya, karena itu agak terbatas pada platform target yang agak tidak spesifik (sistem unixoid ...). Port windows-nya didasarkan pada win2unix-layer eksternal, dan tidak ada IMHO-OSX-layer-kompatibilitas sama sekali. Semua hal ini mengarahkan dengan jelas ke arah yang salah, setidaknya untuk kode saya (pada tingkat lengkung ini ...) ;-) Jadi, Glib bukan pilihan
Frunsi
9
Cari, ganti, dan seterusnya berfungsi dengan baik pada string UTF-8 (bagian dari urutan byte yang mewakili karakter tidak pernah dapat disalahartikan sebagai karakter lain). Kenyataannya, UTF-16 dan UTF-32 sama sekali tidak membuat ini lebih mudah: ketiga pengkodean adalah pengkodean multibyte dalam praktiknya, karena karakter yang dirasakan pengguna (cluster grapheme) dapat berupa jumlah codec unicode yang panjang! Solusi pragmatis adalah menggunakan UTF-8 untuk semuanya, dan mengonversi ke UTF-16 hanya ketika berhadapan dengan Windows API.
Daniel
5
@ Frunsi: Cari dan ganti karya sama baiknya dengan UTF-8 dan UTF-32. Justru karena pemrosesan teks Unicode-aware yang tepat perlu berurusan dengan multi-codepoint 'karakter', yang menggunakan pengkodean panjang variabel seperti UTF-8 tidak membuat pemrosesan string semakin rumit. Jadi gunakan saja UTF-8 di mana-mana. Fungsi string C normal akan berfungsi dengan baik pada UTF-8 (dan sesuai dengan perbandingan ordinal pada string Unicode), dan jika Anda memerlukan sesuatu yang lebih sadar bahasa, Anda harus memanggil perpustakaan Unicode, UTF-16/32 tidak bisa menyelamatkanmu dari itu.
Daniel
25
  1. Ketika Anda ingin memiliki karakter lebar disimpan di string Anda. widetergantung pada implementasinya. Visual C ++ default ke 16 bit jika saya ingat dengan benar, sedangkan GCC default tergantung pada target. Panjangnya 32 bit di sini. Harap dicatat wchar_t (tipe karakter lebar) tidak ada hubungannya dengan unicode. Ini hanya dijamin bahwa ia dapat menyimpan semua anggota set karakter terbesar yang didukung oleh implementasi oleh lokalnya, dan setidaknya selama char. Anda dapat menyimpan string unicode dengan baik agar dapat std::stringmenggunakan utf-8enkode. Tapi itu tidak akan mengerti arti dari poin kode unicode. Begitustr.size()tidak akan memberi Anda jumlah karakter logis dalam string Anda, tetapi hanya jumlah elemen char atau wchar_t yang disimpan dalam string / wstring tersebut. Untuk alasan itu, orang-orang wrapper gtk / glib C ++ telah mengembangkan Glib::ustringkelas yang dapat menangani utf-8.

    Jika panjang wchar_t Anda adalah 32 bit, maka Anda dapat menggunakan utf-32sebagai pengkodean unicode, dan Anda dapat menyimpan dan menangani string unicode menggunakan pengkodean tetap (utf-32 adalah panjang tetap). Ini berarti s.size()fungsi wstring Anda kemudian akan mengembalikan jumlah elemen wchar_t dan karakter logis yang tepat.

  2. Ya, char selalu setidaknya 8 bit, yang berarti dapat menyimpan semua nilai ASCII.
  3. Ya, semua kompiler utama mendukungnya.
Johannes Schaub - litb
sumber
Saya ingin tahu tentang # 2. Saya pikir 7 bit secara teknis akan valid juga? Atau apakah itu diperlukan untuk dapat menyimpan sesuatu melewati 7-bit karakter ASCII?
jalf
1
ya, jalf. c89 menetapkan rentang minimal untuk tipe dasar dalam dokumentasi limit.h (untuk karakter yang tidak ditandatangani, yaitu 0..255 mnt), dan sistem biner murni untuk tipe integer. mengikuti char, unsigned char dan char yang ditandatangani memiliki panjang bit minimum 8. c ++ mewarisi aturan tersebut.
Johannes Schaub - litb
15
"Ini berarti fungsi s.size () wstring Anda kemudian akan mengembalikan jumlah elemen wchar_t dan karakter logis yang tepat." Ini tidak sepenuhnya akurat, bahkan untuk Unicode. Akan lebih akurat untuk mengatakan codepoint daripada "karakter logis", bahkan dalam UTF-32 karakter yang diberikan dapat terdiri dari beberapa codepoint.
Logan Capaldo
Apakah kalian pada dasarnya mengatakan bahwa C ++ tidak memiliki dukungan asli untuk rangkaian karakter Unicode?
Mihai Danila
1
"Tapi itu tidak akan mengerti arti dari titik kode unicode." Di windows, tidak juga std::wstring.
Deduplikator
5

Saya sering menggunakan std :: string untuk menampung karakter utf-8 tanpa masalah sama sekali. Saya sungguh-sungguh merekomendasikan melakukan ini ketika berinteraksi dengan API yang menggunakan utf-8 sebagai tipe string asli juga.

Sebagai contoh, saya menggunakan utf-8 ketika menghubungkan kode saya dengan juru bahasa Tcl.

Peringatan utama adalah panjang string std ::, tidak lagi jumlah karakter dalam string.


sumber
1
Juan: Maksud Anda string std :: dapat menampung semua karakter unicode tetapi panjangnya akan dilaporkan salah? Apakah ada alasan pelaporan panjang yang salah?
3
Saat menggunakan encoding utf-8, karakter unicode tunggal dapat terdiri dari beberapa byte. Inilah sebabnya mengapa pengkodean utf-8 lebih kecil ketika menggunakan sebagian besar karakter dari set ascii standar. Anda perlu menggunakan fungsi khusus (atau memutar sendiri) untuk mengukur jumlah karakter unicode.
2
(Khusus Windows) Sebagian besar fungsi akan mengharapkan bahwa string yang menggunakan byte adalah ASCII dan 2 byte adalah Unicode, MBCS versi yang lebih lama. Yang berarti jika Anda menyimpan 8 bit unicode Anda harus mengonversi ke 16 bit unicode untuk memanggil fungsi windows standar (kecuali Anda hanya menggunakan bagian ASCII).
Greg Domjan
2
Tidak hanya std :: string yang melaporkan panjangnya tidak benar, tetapi juga akan menampilkan string yang salah. Jika beberapa karakter Unicode direpresentasikan dalam UTF-8 sebagai beberapa byte, yang dianggap std :: string sebagai karakternya sendiri, maka rutinitas manipulasi string std :: Anda mungkin akan menampilkan beberapa karakter aneh yang dihasilkan dari kesalahan interpretasi karakter. karakter yang benar.
Mihai Danila
2
Saya menyarankan mengubah jawaban untuk menunjukkan bahwa string harus dianggap hanya sebagai wadah byte, dan, jika byte adalah beberapa pengkodean Unicode (UTF-8, UTF-16, ...), maka Anda harus menggunakan perpustakaan tertentu yang mengerti bahwa. API berbasis string standar (panjang, substr, dll.) Akan gagal total dengan karakter multibyte. Jika pembaruan ini dibuat, saya akan menghapus downvote saya.
Mihai Danila
4
  1. Saat Anda ingin menyimpan karakter 'lebar' (Unicode).
  2. Ya: 255 dari mereka (tidak termasuk 0).
  3. Iya.
  4. Inilah artikel pengantar: http://www.joelonsoftware.com/articles/Unicode.html
ChrisW
sumber
11
std :: string dapat menampung 0 baik-baik saja (hanya hati-hati jika Anda memanggil metode c_str ())
Mr Fooz
3
Dan sebenarnya, char tidak dijamin 8 bit. :) Tautan Anda di # 4 harus dibaca, tapi saya rasa itu tidak menjawab pertanyaan. Karakter lebar tidak ada hubungannya dengan unicode. Ini hanyalah karakter yang lebih luas. (Seberapa jauh lebih luas tergantung pada OS, tetapi biasanya 16 atau 32 bit)
jalf
2
  1. ketika Anda ingin menggunakan string Unicode dan bukan hanya ascii, membantu untuk internasionalisasi
  2. ya, tapi itu tidak cocok dengan 0
  3. tidak menyadari ada yang tidak
  4. karakter lebar adalah cara khusus kompiler menangani representasi panjang tetap dari karakter unicode, untuk MSVC itu adalah karakter 2 byte, untuk gcc saya mengerti itu adalah 4 byte. dan +1 untuk http://www.joelonsoftware.com/articles/Unicode.html
Greg Domjan
sumber
1
2. Sebuah std :: string dapat menampung karakter NULL dengan baik. Itu juga dapat menampung utf-8 dan karakter lebar juga.
@Juan: Itu membuat saya kebingungan lagi. Jika std :: string dapat menyimpan karakter unicode, apa yang istimewa dengan std :: wstring
1
@Appu: std :: string dapat menampung karakter unicode UTF-8. Ada sejumlah standar unicode yang ditargetkan pada lebar karakter yang berbeda. UTf8 adalah lebar 8 bit. Ada juga UTF-16 dan UTF-32 masing
Greg D
Dengan std :: wstring. Setiap karakter unicode bisa menjadi satu wchar_t saat menggunakan penyandian panjang tetap. Misalnya, jika Anda memilih untuk menggunakan joel pada pendekatan perangkat lunak sebagai tautan ke Greg. Maka panjang wstring adalah jumlah karakter unicode tepat dalam string. Tetapi membutuhkan lebih banyak ruang
Saya tidak mengatakan itu tidak dapat menahan 0 '\ 0', dan apa yang saya maksud dengan tidak bermain dengan baik adalah bahwa beberapa metode mungkin tidak memberi Anda hasil yang diharapkan berisi semua data wstring. Sangat keras pada suara bawah.
Greg Domjan
2

Aplikasi yang tidak puas dengan hanya 256 karakter berbeda memiliki opsi untuk menggunakan karakter lebar (lebih dari 8 bit) atau pengkodean panjang variabel (pengkodean multibyte dalam terminologi C ++) seperti UTF-8. Karakter lebar umumnya membutuhkan lebih banyak ruang daripada pengodean panjang variabel, tetapi lebih cepat diproses. Aplikasi multi-bahasa yang memproses teks dalam jumlah besar biasanya menggunakan karakter lebar saat memproses teks, tetapi mengubahnya menjadi UTF-8 saat menyimpannya ke disk.

Satu-satunya perbedaan antara a stringdan a wstringadalah tipe data dari karakter yang mereka simpan. Sebuah string menyimpan charukuran yang dijamin setidaknya 8 bit, sehingga Anda dapat menggunakan string untuk memproses misalnya teks ASCII, ISO-8859-15, atau UTF-8. Standar tidak mengatakan apa-apa tentang set karakter atau pengodean.

Praktis setiap kompiler menggunakan set karakter yang 128 karakter pertamanya sesuai dengan ASCII. Ini juga terjadi dengan kompiler yang menggunakan pengkodean UTF-8. Hal penting yang harus diperhatikan ketika menggunakan string dalam UTF-8 atau pengkodean panjang variabel lainnya, adalah bahwa indeks dan panjangnya diukur dalam byte, bukan karakter.

Tipe data wstring adalah wchar_t, yang ukurannya tidak didefinisikan dalam standar, kecuali bahwa itu harus setidaknya sebesar char, biasanya 16 bit atau 32 bit. wstring dapat digunakan untuk memproses teks dalam implementasi pengkodean karakter lebar yang ditentukan. Karena pengkodean tidak didefinisikan dalam standar, tidak mudah untuk mengkonversi antara string dan wstrings. Seseorang tidak dapat menganggap wstrings memiliki pengkodean dengan panjang tetap yang baik.

Jika Anda tidak memerlukan dukungan multi-bahasa, Anda mungkin baik-baik saja dengan menggunakan string biasa. Di sisi lain, jika Anda menulis aplikasi grafis, seringkali API hanya mendukung karakter lebar. Maka Anda mungkin ingin menggunakan karakter lebar yang sama saat memproses teks. Ingatlah bahwa UTF-16 adalah pengodean panjang variabel, artinya Anda tidak dapat berasumsi length()untuk mengembalikan jumlah karakter. Jika API menggunakan penyandian dengan panjang tetap, seperti UCS-2, pemrosesan menjadi mudah. Mengubah antara karakter lebar dan UTF-8 sulit dilakukan dengan cara portabel, tetapi sekali lagi, antarmuka pengguna API Anda mungkin mendukung konversi.

Seppo Enarvi
sumber
Jadi, parafrase paragraf pertama: Aplikasi yang membutuhkan lebih dari 256 karakter harus menggunakan pengkodean multibyte atau pengkodean mungkin_multibyte.
Deduplicator
Namun, umumnya penyandian 16 dan 32 bit seperti UCS-2 dan UCS-4 tidak disebut penyandian multibyte. Standar C ++ membedakan antara pengkodean multibyte dan karakter lebar. Representasi karakter lebar menggunakan angka tetap (umumnya lebih dari 8) bit per karakter. Pengkodean yang menggunakan byte tunggal untuk mengkodekan karakter yang paling umum, dan beberapa byte untuk mengkodekan sisa rangkaian karakter, disebut pengkodean multibyte.
Seppo Enarvi
Maaf, komentar ceroboh. Seharusnya mengatakan pengodean panjang variabel. UTF-16 adalah pengodean panjang variabel, seperti halnya UTF-8. Berpura-pura itu bukan ide yang buruk .
Deduplicator
Itu poin yang bagus. Tidak ada alasan mengapa wstrings tidak dapat digunakan untuk menyimpan UTF-16 (bukan UCS-2), tetapi kemudian kenyamanan pengkodean panjang terprogram hilang.
Seppo Enarvi
2

Pertanyaan yang bagus Saya pikir DATA ENCODING (terkadang CHARSET juga terlibat) adalah MECHANISME EKSPRESI MEMORY untuk menyimpan data ke file atau mentransfer data melalui jaringan, jadi saya menjawab pertanyaan ini sebagai:

1. Kapan saya harus menggunakan std :: wstring over std :: string?

Jika platform pemrograman atau fungsi API adalah byte tunggal, dan kami ingin memproses atau mem-parsing beberapa data Unicode, misalnya membaca dari Windows'.REG file atau jaringan aliran 2-byte, kita harus mendeklarasikan variabel std :: wstring agar mudah memprosesnya. misal: wstring ws = L "中国 a" (memori 6 oktet: 0x4E2D 0x56FD 0x0061), kita dapat menggunakan ws [0] untuk mendapatkan karakter '中' dan ws [1] untuk mendapatkan karakter '国' dan ws [2] untuk dapatkan karakter 'a', dll.

2. Bisakah std :: string menampung seluruh rangkaian karakter ASCII, termasuk karakter khusus?

Iya. Tetapi perhatikan: American ASCII, berarti setiap oktet 0x00 ~ 0xFF adalah singkatan dari satu karakter, termasuk teks yang dapat dicetak seperti "123abc & * _ &" dan Anda mengatakan yang spesial, kebanyakan mencetaknya sebagai '.' hindari editor atau terminal yang membingungkan. Dan beberapa negara lain memperluas rangkaian karakter "ASCII" mereka sendiri, misalnya bahasa Cina, menggunakan 2 oktet untuk mewakili satu karakter.

3. Apakah std :: wstring didukung oleh semua kompiler C ++ yang populer?

Mungkin, atau kebanyakan. Saya telah menggunakan: VC ++ 6 dan GCC 3.3, YES

4. Apa yang dimaksud dengan "karakter lebar"?

karakter lebar sebagian besar menunjukkan penggunaan 2 oktet atau 4 oktet untuk menampung semua karakter negara. 2 oktet UCS2 adalah sampel yang representatif, dan selanjutnya mis. Bahasa Inggris 'a', memorinya 2 oktet 0x0061 (vs dalam ASCII 'memori adalah 1 oktet 0x61)

Leiyi. Cina
sumber
0

Ada beberapa jawaban yang sangat bagus di sini, tetapi saya pikir ada beberapa hal yang dapat saya tambahkan mengenai Windows / Visual Studio. Ini didasarkan pada pengalaman saya dengan VS2015. Di Linux, pada dasarnya jawabannya adalah menggunakan UTF-8 yang disandikan di std::stringmana-mana. Pada Windows / VS itu menjadi lebih kompleks. Inilah sebabnya. Windows mengharapkan string yang disimpan menggunakan chars untuk dikodekan menggunakan codepage lokal. Ini hampir selalu set karakter ASCII diikuti oleh 128 karakter khusus lainnya tergantung pada lokasi Anda. Biarkan saya hanya menyatakan bahwa ini tidak hanya ketika menggunakan Windows API, ada tiga tempat utama lainnya di mana string ini berinteraksi dengan standar C ++. Ini adalah string literal, output untuk std::coutmenggunakan <<dan meneruskan nama file std::fstream.

Saya akan berada di depan di sini bahwa saya seorang programmer, bukan spesialis bahasa. Saya menghargai bahwa USC2 dan UTF-16 tidak sama, tetapi untuk tujuan saya mereka cukup dekat untuk dipertukarkan dan saya menggunakannya di sini. Saya tidak benar-benar yakin yang menggunakan Windows, tapi saya biasanya tidak perlu tahu juga. Saya telah menyatakan UCS2 dalam jawaban ini, sangat menyesal sebelumnya jika saya mengecewakan siapa pun dengan ketidaktahuan saya tentang masalah ini dan saya senang mengubahnya jika ada kesalahan.

Literal string

Jika Anda memasukkan string literal yang hanya berisi karakter yang dapat direpresentasikan oleh codepage Anda, maka VS menyimpannya dalam file Anda dengan 1 byte per pengkodean karakter berdasarkan codepage Anda. Perhatikan bahwa jika Anda mengubah codepage Anda atau memberikan sumber Anda ke pengembang lain menggunakan halaman kode yang berbeda maka saya pikir (tetapi belum diuji) bahwa karakter akan berakhir berbeda. Jika Anda menjalankan kode di komputer menggunakan halaman kode yang berbeda maka saya tidak yakin apakah karakternya juga akan berubah.

Jika Anda memasukkan string literal yang tidak dapat diwakili oleh codepage Anda maka VS akan meminta Anda untuk menyimpan file sebagai Unicode. File tersebut kemudian akan dikodekan sebagai UTF-8. Ini berarti bahwa semua karakter Non ASCII (termasuk yang ada di codepage Anda) akan diwakili oleh 2 atau lebih byte. Ini berarti jika Anda memberikan sumber Anda kepada orang lain, sumbernya akan terlihat sama. Namun, sebelum meneruskan sumber ke kompiler, VS mengubah teks yang disandikan UTF-8 ke halaman kode teks yang disandikan dan setiap karakter yang hilang dari halaman kode diganti dengan ?.

Satu-satunya cara untuk menjamin dengan benar merepresentasikan string string Unicode dalam VS adalah mendahului string literal dengan Lmembuatnya menjadi string string yang luas. Dalam hal ini VS akan mengubah teks yang disandikan UTF-8 dari file menjadi UCS2. Anda kemudian harus meneruskan string literal ini ke std::wstringkonstruktor atau Anda perlu mengubahnya menjadi utf-8 dan memasukkannya ke dalam std::string. Atau jika mau, Anda dapat menggunakan fungsi Windows API untuk menyandikannya menggunakan halaman kode Anda untuk memasukkannya std::string, tetapi Anda mungkin juga belum menggunakan string string literal.

std :: cout

Saat mengeluarkan ke konsol menggunakan <<Anda hanya dapat menggunakan std::string, tidak std::wstringdan teks harus dikodekan menggunakan codepage lokal Anda. Jika sudah, std::wstringmaka Anda harus mengonversinya menggunakan salah satu fungsi Windows API dan karakter apa pun yang tidak ada di codepage Anda diganti ?(mungkin Anda dapat mengubah karakter, saya tidak ingat).

std :: nama file fstream

OS Windows menggunakan UCS2 / UTF-16 untuk nama file-nya jadi apa pun codepage Anda, Anda dapat memiliki file dengan karakter Unicode apa pun. Tetapi ini berarti bahwa untuk mengakses atau membuat file dengan karakter yang tidak ada pada codepage Anda, Anda harus menggunakannya std::wstring. Tidak ada jalan lain. Ini adalah ekstensi khusus Microsoft untuk std::fstreamjadi mungkin tidak dapat dikompilasi pada sistem lain. Jika Anda menggunakan std :: string maka Anda hanya dapat menggunakan nama file yang hanya menyertakan karakter pada codepage Anda.

Opsi Anda

Jika Anda hanya bekerja di Linux maka Anda mungkin tidak sampai sejauh ini. Cukup gunakan UTF-8 di std::stringmana - mana.

Jika Anda hanya bekerja di Windows, gunakan UCS2 di std::wstringmana-mana. Beberapa puritan mungkin mengatakan menggunakan UTF8 kemudian mengonversi ketika dibutuhkan, tetapi mengapa repot-repot dengan kerumitan.

Jika Anda lintas platform maka itu berantakan untuk terus terang. Jika Anda mencoba menggunakan UTF-8 di mana-mana di Windows maka Anda harus benar-benar berhati-hati dengan string literal Anda dan output ke konsol. Anda dapat dengan mudah merusak string Anda di sana. Jika Anda menggunakan di std::wstringmana-mana di Linux maka Anda mungkin tidak memiliki akses ke versi yang luas std::fstream, jadi Anda harus melakukan konversi, tetapi tidak ada risiko korupsi. Jadi secara pribadi saya pikir ini adalah pilihan yang lebih baik. Banyak yang akan tidak setuju, tapi saya tidak sendirian - itu jalan yang diambil oleh wxWidgets misalnya.

Pilihan lain bisa dengan mengetikkan unicodestringseperti std::stringdi Linux dan std::wstringdi Windows, dan memiliki makro yang disebut UNI () yang awalan L pada Windows dan tidak ada di Linux, maka kode

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

akan baik-baik saja di kedua platform saya pikir.

Jawaban

Jadi Untuk menjawab pertanyaan Anda

1) Jika Anda memprogram untuk Windows, maka setiap saat, jika lintas platform maka mungkin sepanjang waktu, kecuali jika Anda ingin menangani kemungkinan masalah korupsi pada Windows atau menulis beberapa kode dengan platform khusus #ifdefsuntuk mengatasi perbedaan, jika hanya menggunakan Linux tidak pernah.

2) Ya. Selain itu di Linux Anda dapat menggunakannya untuk semua Unicode juga. Pada Windows Anda hanya dapat menggunakannya untuk semua unicode jika Anda memilih untuk secara manual menyandi menggunakan UTF-8. Tetapi Windows API dan standar C ++ kelas akan mengharapkan std::stringuntuk dikodekan menggunakan codepage lokal. Ini termasuk semua ASCII plus 128 karakter lain yang berubah tergantung pada codepage yang diatur oleh komputer Anda untuk digunakan.

3) Saya percaya begitu, tetapi jika tidak maka itu hanya typedef sederhana dari 'std :: basic_string' menggunakan wchar_tbukanchar

4) Karakter lebar adalah tipe karakter yang lebih besar dari chartipe standar 1 byte . Di Windows 2 byte, di Linux 4 byte.

Phil Rosenberg
sumber
1
Mengenai "Namun, sebelum meneruskan sumber ke kompiler, VS mengubah teks yang disandikan UTF-8 ke halaman kode teks yang disandikan dan karakter apa pun yang hilang dari halaman kode diganti dengan?" -> Saya tidak berpikir bahwa ini benar ketika kompiler menggunakan pengkodean UTF-8 (gunakan /utf-8).
Roi Danton
Saya tidak menyadari ini sebagai opsi. Dari tautan ini docs.microsoft.com/en-us/cpp/build/reference/... tampaknya tidak ada kotak centang untuk dipilih di properti proyek, Anda harus menambahkannya sebagai opsi baris perintah tambahan. Tempat yang bagus!
Phil Rosenberg
-2

1) Seperti yang disebutkan oleh Greg, wstring sangat membantu untuk internasionalisasi, saat itulah Anda akan merilis produk Anda dalam bahasa selain bahasa Inggris

4) Periksa ini untuk karakter lebar http://en.wikipedia.org/wiki/Wide_character

Raghu
sumber
-6

Kapan sebaiknya Anda TIDAK menggunakan karakter lebar?

Ketika Anda sedang menulis kode sebelum tahun 1990.

Jelas, saya sedang jungkir balik, tapi sungguh, ini abad ke-21 sekarang. 127 karakter sudah lama tidak lagi mencukupi. Ya, Anda bisa menggunakan UTF8, tapi mengapa repot dengan sakit kepala?


sumber
16
@ Dave: Saya tidak tahu sakit kepala apa yang dibuat UTF-8 yang lebih besar daripada Widechars (UTF-16). di UTF-16, Anda juga memiliki karakter multi-karakter.
Pavel Radzivilovsky
Masalahnya adalah bahwa jika Anda berada di mana saja selain negara berbahasa Inggris, Anda HARUS menggunakan wchar_t. Belum lagi beberapa huruf memiliki lebih banyak karakter daripada yang dapat Anda masukkan ke dalam byte. Kami berada di sana, di DOS. Skepage skizofrenia, tidak, terima kasih, tidak lebih ..
Swift - Friday Pie
1
@Swift Masalahnya wchar_tadalah ukuran dan artinya khusus untuk OS. Itu hanya menukar masalah lama dengan yang baru. Sedangkan a charadalah charterlepas dari OS (pada platform yang sama, setidaknya). Jadi kita bisa saja menggunakan UTF-8, mengemas semuanya menjadi urutan chars, dan menyesalkan bagaimana C ++ membuat kita benar-benar sendirian tanpa metode standar untuk mengukur, mengindeks, menemukan dll dalam urutan seperti itu.
underscore_d
1
@Swift Anda sepertinya sudah benar-benar mundur. wchar_tadalah tipe data dengan lebar tetap, sehingga array 10 wchar_takan selalu menempati sizeof(wchar_t) * 10byte platform. Dan UTF-16 adalah pengkodean lebar variabel di mana karakter dapat terdiri dari 1 atau 2 codepoint 16-bit (dan s / 16/8 / g untuk UTF-8).
underscore_d
1
@SteveHollasch wchar_t representasi string di windows akan mengkodekan karakter lebih besar dari FFFF sebagai pasangan pengganti utama, yang lain hanya akan mengambil satu elemen wchar_t. Sehingga representasi itu tidak akan kompatibel dengan representasi yang dibuat oleh kompiler gnu (di mana semua karakter kurang dari FFFF akan memiliki nol kata di depan mereka). Apa yang disimpan di wchar_t ditentukan oleh programmer dan compiler, bukan berdasarkan kesepakatan
Swift - Friday Pie