Penggunaan 'use utf8;' memberi saya 'Karakter luas dalam cetakan'

86

Jika saya menjalankan program Perl berikut:

perl -e 'use utf8; print "鸡\n";'

Saya mendapatkan peringatan ini:

Wide character in print at -e line 1.

Jika saya menjalankan program Perl ini:

perl -e 'print "鸡\n";'

Saya tidak mendapat peringatan.

Saya pikir use utf8saya harus menggunakan karakter UTF-8 dalam skrip Perl. Mengapa ini tidak berhasil dan bagaimana cara memperbaikinya? Saya menggunakan Perl 5.16.2. Saya memiliki masalah yang sama jika ini ada dalam file alih-alih menjadi satu liner pada baris perintah.

Eric Johnson
sumber
3
"Mengapa ini tidak berhasil?" Ini tidak bekerja, tapi sudah pengalaman saya dengan Unicode bahwa ada banyak program sangat rusak di luar sana yang terlihat seperti mereka sedang bekerja. Jika Anda memperbaiki satu hal, membuat kode menjadi sedikit kurang salah, hasilnya tampak jauh lebih buruk. Hanya ketika Anda memperbaiki bagian terakhir , semuanya terlihat baik kembali.
Hobbs

Jawaban:

110

Tanpa use utf8Perl menafsirkan string Anda sebagai urutan karakter byte tunggal. Ada empat byte dalam string Anda seperti yang Anda lihat dari ini:

$ perl -E 'say join ":", map { ord } split //, "鸡\n";'
233:184:161:10

Tiga byte pertama membentuk karakter Anda, yang terakhir adalah feed baris.

Panggilan untuk printmengirimkan empat karakter ini ke STDOUT. Konsol Anda kemudian mengetahui cara menampilkan karakter ini. Jika konsol Anda disetel untuk menggunakan UTF8, maka tiga byte tersebut akan ditafsirkan sebagai karakter tunggal Anda dan itulah yang ditampilkan.

Jika kita menambahkan utf8modul, semuanya berbeda. Dalam hal ini, Perl menafsirkan string Anda hanya sebagai dua karakter.

$ perl -Mutf8 -E 'say join ":", map { ord } split //, "鸡\n";'
40481:10

Secara default, lapisan IO Perl mengasumsikan bahwa ia bekerja dengan karakter byte tunggal. Jadi ketika Anda mencoba untuk mencetak karakter multi-byte, Perl berpikir ada sesuatu yang salah dan memberi Anda peringatan. Seperti biasa, Anda bisa mendapatkan lebih banyak penjelasan untuk kesalahan ini dengan menyertakan use diagnostics. Ini akan mengatakan ini:

(S utf8) Perl bertemu dengan karakter yang luas (> 255) ketika tidak diharapkan. Peringatan ini secara default aktif untuk I / O (seperti cetakan). Cara termudah untuk menghentikan peringatan ini adalah dengan menambahkan lapisan: utf8 ke keluaran, misalnya binmode STDOUT, ': utf8'. Cara lain untuk mematikan peringatan adalah dengan tidak menambahkan peringatan 'utf8'; tetapi itu sering kali lebih mendekati kecurangan. Secara umum, Anda seharusnya menandai filehandle secara eksplisit dengan sebuah encoding, lihat open dan perlfunc / binmode.

Seperti yang ditunjukkan orang lain, Anda perlu memberi tahu Perl untuk menerima keluaran multi-byte. Ada banyak cara untuk melakukan ini (lihat Tutorial Perl Unicode untuk beberapa contoh). Salah satu cara paling sederhana adalah dengan menggunakan tanda -CSbaris perintah - yang memberi tahu tiga penanganan file standar (STDIN, STDOUT dan STDERR) untuk menangani UTF8.

$ perl -Mutf8 -e 'print "鸡\n";'
Wide character in print at -e line 1.
鸡

vs.

$ perl -Mutf8 -CS -e 'print "鸡\n";'

Unicode adalah area yang besar dan kompleks. Seperti yang Anda lihat, banyak program sederhana tampaknya melakukan hal yang benar, tetapi untuk alasan yang salah. Ketika Anda mulai memperbaiki bagian dari program, keadaan akan sering menjadi lebih buruk sampai Anda telah memperbaiki semua program.

Dave Cross
sumber
Bagaimana cara mengeja -Mutf8jika tidak dalam satu liner perl?
Lei Yang
@LeiYang:use utf8;
Dave Cross
80

Semua use utf8;tidak memberi tahu Perl bahwa kode sumber dikodekan menggunakan UTF-8. Anda perlu memberi tahu Perl cara menyandikan teks Anda:

use open ':std', ':encoding(UTF-8)';
ikegami
sumber
Terima kasih, ini berfungsi dengan baik untuk program yang disimpan dalam file, dibandingkan dengan satu baris pada baris perintah, yang mencakup jawaban @ DaveCross.
vktec
19

Encode semua keluaran standar sebagai UTF-8:

binmode STDOUT, ":utf8";
Boris Ivanov
sumber
2
use open ':std', ':encoding(UTF-8)';seperti yang diajukan oleh jawaban lain melakukan ini untuk STDOUT tetapi juga menandai STDERR, dan STDIN sebagai UTF-8, jadi Anda mendapatkan tiga untuk harga satu pernyataan. Lihat juga stackoverflow.com/a/42194059
Stephen Ostermiller
Setuju. Ini lebih baik.
Boris Ivanov
14

Anda bisa mendekati "lakukan saja utf8 di mana saja" dengan menggunakan modul CPAN utf8::all.

perl -Mutf8::all -e 'print "鸡\n";'

Saat printmenerima sesuatu yang tidak dapat dicetak (karakter lebih besar dari 255 jika tidak ada :encodinglapisan yang disediakan), ia menganggap Anda bermaksud untuk mengenkodenya menggunakan UTF-8. Ia melakukannya, setelah peringatan tentang masalah tersebut.

Joel Berger
sumber
5

Anda bisa menggunakan ini,

perl -CS filename.

Ini juga akan menghentikan kesalahan itu.

Karthikeyan.RS
sumber
hanya ini yang membantu
muenalan
0

Dalam bahasa Spanyol Anda dapat menemukan kesalahan ini ketika di samping mulai menggunakan:

use utf8;

Encoding editor Anda berada dalam encoding yang berbeda. Jadi apa yang Anda lihat di editor bukanlah apa yang Perl lakukan. Untuk mengatasi kesalahan itu, cukup ubah encoding editor ke Unicode / UTF-8 .

DiegoAr
sumber
1
Tidak. Bukan ini yang menyebabkan kesalahan. Kode semuanya dikodekan dengan benar sebagai UTF8 tetapi penanganan file keluaran tidak tahu bahwa itu.
Dave Cross