Nasib menyedihkan specifier format fungsi printf untuk karakter Unicode di Visual C ++

Dukungan Unicode pada Windows muncul lebih awal daripada kebanyakan sistem operasi lain. Karena itu, banyak masalah yang terkait dengan representasi karakter di Windows tidak diselesaikan dengan cara yang sama seperti pada sistem lain yang pengembangnya menunda implementasi standar baru sampai waktu yang lebih baik [1]. Contoh paling jelas: pada Windows, pengkodean UCS-2 digunakan untuk mewakili karakter Unicode. Direkomendasikan oleh Konsorsium Unicode karena versi 1.0 hanya mendukung 65.536 karakter [2]. Lima tahun kemudian, Konsorsium berubah pikiran, tetapi pada saat itu sudah terlambat untuk mengubah sesuatu di Windows, karena Win32s, Windows NT 3.1, Windows NT 3.5, Windows NT 3.51 dan Windows 95 telah dirilis ke pasar - mereka semua menggunakan pengkodean UCS -2 [3].

Tetapi hari ini kita akan berbicara tentang rangkaian format fungsi printf .

Karena Unicode diadopsi pada Windows lebih awal daripada di C, ini berarti bahwa pengembang Microsoft harus mencari cara untuk menerapkan dukungan untuk standar ini dalam runtime C. Akibatnya, fitur seperti wcscmp , wcschr, dan wprintf muncul . Adapun untuk memformat string di printf , kualifikasi berikut diperkenalkan untuk mereka:

  • % s mewakili string dengan lebar yang sama dengan string format;
  • % S mewakili string dengan lebar terbalik dengan lebar string format;
  • % hs mewakili string biasa terlepas dari lebar string format;
  • % ws dan % ls mewakili string lebar terlepas dari lebar string format.

Idenya adalah menulis kode seperti ini:

TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %s.\n"), buffer); 

Dan saat kompilasi dalam mode ANSI, dapatkan hasil ini:

 char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %s.\n", buffer); 

Dan ketika mengkompilasi dalam mode Unicode - ini [4]:

 wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer); 

Karena specifier % s menerima string dengan lebar yang sama dengan string format, kode ini akan berfungsi dengan baik dalam format ANSI dan Unicode. Juga, solusi ini sangat menyederhanakan konversi kode yang sudah ditulis dari format ANSI ke format Unicode, karena string dari lebar yang diperlukan diganti untuk penspesifikasi % s .

Ketika dukungan Unicode secara resmi ditambahkan ke C99, komite standardisasi bahasa C mengadopsi model string format yang berbeda untuk fungsi printf :

  • % s dan % hs mewakili string biasa;
  • % ls mewakili string lebar.

Di situlah masalah dimulai. Selama enam tahun terakhir pada saat itu, sejumlah besar program dengan volume miliaran baris ditulis untuk Windows, dan mereka menggunakan format lama. Bagaimana cara menjadi kompiler Visual C dan C ++?

Diputuskan untuk tetap menggunakan model lama yang tidak standar, agar tidak merusak semua program Windows yang ada di dunia.

Jika Anda ingin kode Anda berfungsi di kedua lingkungan runtime yang mematuhi aturan klasik untuk printf dan yang mengikuti aturan standar C, Anda harus membatasi diri Anda pada % hs specifier untuk string reguler dan % ls untuk string lebar. Dalam hal ini, kesegaran hasil dijamin, terlepas dari apakah string format dilewatkan ke fungsi sprintf atau wsprintf .

 #ifdef UNICODE #define TSTRINGWIDTH TEXT("l") #else #define TSTRINGWIDTH TEXT("h") #endif TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %") TSTRINGWIDTH TEXT("s\n"), buffer); char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %hs\n", buffer); wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf("The string is %ls\n", buffer); 

Definisi TSTRINGWIDTH yang terpisah memungkinkan Anda untuk menulis, misalnya, kode ini:

 _tprintf(TEXT("The string is %10") TSTRINGWIDTH TEXT("s\n"), buffer); 

Karena orang-orang menyukai penyajian informasi tabular, inilah tabel untuk Anda.


Saya menyoroti garis dengan kualifikasi, yang didefinisikan dalam C dengan cara yang sama seperti dalam format klasik yang diadopsi di Windows [5]. Gunakan kualifikasi ini jika Anda ingin kode Anda menghasilkan hasil yang sama di kedua format.

Catatan

[1] Tampaknya pengenalan Unicode di Windows sebelum sistem lain seharusnya memberi Microsoft keuntungan dari langkah pertama, tetapi - setidaknya dalam kasus Unicode - itu berubah menjadi "kutukan pelopor" bagi mereka, karena sisanya memutuskan untuk hanya menunggu sampai waktu yang lebih baik, ketika akan ada solusi yang lebih menjanjikan (seperti pengkodean UTF-8), dan hanya setelah itu memperkenalkan Unicode dalam sistem mereka.

[2] Rupanya, mereka percaya bahwa 65.536 karakter seharusnya sudah cukup untuk semua orang .

[3] Itu kemudian digantikan oleh UTF-16. Untungnya, UTF-16 kompatibel dengan UCS-2 untuk karakter kode yang dapat direpresentasikan dalam kedua pengkodean.

[4] Secara resmi, versi Unicode akan terlihat seperti ini:

 unsigned short buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer); 

Faktanya adalah bahwa wchar_t belum menjadi tipe independen, dan sampai ditambahkan ke standar, itu hanya sinonim untuk short unsigned . Liku-liku nasib wchar_t dapat ditemukan di artikel terpisah .

[5] Format klasik yang dikembangkan oleh Windows muncul pertama kali, jadi itu lebih mungkin bahwa standar C harus beradaptasi dengannya, dan bukan sebaliknya.

Catatan Penerjemah

Saya berterima kasih kepada penulis untuk publikasi ini. Sekarang menjadi jelas bagaimana semua kebingungan ini dengan "% s" ternyata. Faktanya adalah bahwa pengguna kami terus-menerus mengajukan pertanyaan mengapa PVS-Studio bereaksi berbeda terhadap kode mereka, seperti yang terlihat bagi mereka, "portabel", tergantung pada apakah mereka mengumpulkan proyek mereka di Linux atau Windows. Itu perlu untuk membuat bagian terpisah khusus dalam deskripsi diagnostik V576 yang ditujukan untuk topik ini (lihat "Garis lebar"). Setelah artikel ini, semuanya menjadi lebih jelas dan jelas. Saya pikir catatan ini harus dibaca untuk semua orang yang mengembangkan aplikasi lintas platform. Baca dan beri tahu kolega.

Source: https://habr.com/ru/post/id466875/


All Articles