การใช้ 'use utf8;' ให้ฉัน 'ตัวอักษรกว้างในการพิมพ์'


86

หากฉันเรียกใช้โปรแกรม Perl ต่อไปนี้:

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

ฉันได้รับคำเตือนนี้:

Wide character in print at -e line 1.

ถ้าฉันเรียกใช้โปรแกรม Perl นี้:

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

ฉันไม่ได้รับคำเตือน

ฉันคิดว่าuse utf8จำเป็นต้องใช้อักขระ UTF-8 ในสคริปต์ Perl เหตุใดจึงไม่ได้ผลและฉันจะแก้ไขได้อย่างไร ฉันใช้ Perl 5.16.2 ฉันมีปัญหาเดียวกันหากสิ่งนี้อยู่ในไฟล์แทนที่จะเป็นซับในบรรทัดคำสั่ง


3
"ทำไมถึงใช้ไม่ได้" มันใช้งานได้ แต่เป็นประสบการณ์ของฉันกับ Unicode ที่มีโปรแกรมที่ใช้งานไม่ได้มากมายที่ดูเหมือนว่าพวกเขากำลังทำงานอยู่ เมื่อคุณแก้ไขสิ่งหนึ่งทำให้โค้ดผิดน้อยลงเล็กน้อยผลลัพธ์จะดูแย่ลงมาก ก็ต่อเมื่อคุณแก้ไขส่วนสุดท้ายที่ทุกอย่างดูดีอีกครั้ง
ฮอบส์

คำตอบ:


110

หากไม่มีuse utf8Perl แปลว่าสตริงของคุณเป็นลำดับของอักขระไบต์เดียว สตริงของคุณมีสี่ไบต์ดังที่คุณเห็นจากสิ่งนี้:

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

สามไบต์แรกประกอบเป็นตัวละครของคุณอันสุดท้ายคือ line-feed

การเรียกให้printส่งอักขระทั้งสี่นี้ไปยัง STDOUT จากนั้นคอนโซลของคุณจะหาวิธีแสดงอักขระเหล่านี้ หากคอนโซลของคุณถูกตั้งค่าให้ใช้ UTF8 ก็จะแปลว่าทั้งสามไบต์นั้นเป็นอักขระเดี่ยวของคุณและนั่นคือสิ่งที่แสดง

หากเราเพิ่มในutf8โมดูลสิ่งต่างๆก็แตกต่างกันไป ในกรณีนี้ Perl ตีความสตริงของคุณเป็นเพียงสองอักขระ

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

โดยค่าเริ่มต้นเลเยอร์ IO ของ Perl จะถือว่าทำงานกับอักขระไบต์เดี่ยว ดังนั้นเมื่อคุณพยายามพิมพ์อักขระแบบหลายไบต์ Perl จะคิดว่ามีบางอย่างผิดปกติและแจ้งเตือนคุณ use diagnosticsเช่นเคยคุณจะได้รับคำอธิบายเพิ่มเติมสำหรับข้อผิดพลาดนี้ได้โดยรวม มันจะบอกว่า:

(S utf8) Perl พบอักขระแบบกว้าง (> 255) เมื่อไม่ได้คาดหวัง คำเตือนนี้เป็นค่าเริ่มต้นสำหรับ I / O (เช่นการพิมพ์) วิธีที่ง่ายที่สุดในการปิดคำเตือนนี้คือการเพิ่มเลเยอร์: utf8 ในเอาต์พุตเช่น binmode STDOUT, ': utf8' อีกวิธีหนึ่งในการปิดคำเตือนคือการไม่เพิ่มคำเตือน 'utf8'; แต่นั่นมักจะใกล้เคียงกับการโกงมากกว่า โดยทั่วไปคุณควรทำเครื่องหมายที่ filehandle ด้วยการเข้ารหัสอย่างชัดเจนโปรดดู open และ perlfunc / binmode

ตามที่คนอื่น ๆ ชี้ให้เห็นว่าคุณต้องบอกให้ Perl ยอมรับเอาต์พุตแบบหลายไบต์ มีหลายวิธีในการดำเนินการนี้ (ดูบทช่วยสอน Perl Unicodeสำหรับตัวอย่างบางส่วน) วิธีที่ง่ายที่สุดวิธีหนึ่งคือการใช้-CSแฟล็กบรรทัดคำสั่งซึ่งจะบอกว่า filehandles มาตรฐานสามแบบ (STDIN, STDOUT และ STDERR) เพื่อจัดการกับ UTF8

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

เทียบกับ

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

Unicode เป็นพื้นที่ขนาดใหญ่และซับซ้อน อย่างที่คุณเห็นโปรแกรมง่ายๆหลายโปรแกรมดูเหมือนจะทำสิ่งที่ถูกต้อง แต่ด้วยเหตุผลที่ไม่ถูกต้อง เมื่อคุณเริ่มที่จะแก้ไขส่วนหนึ่งของโปรแกรมสิ่งที่มักจะได้รับที่เลวร้ายยิ่งจนกว่าคุณจะได้รับการแก้ไขทั้งหมดของโปรแกรม


จะสะกด-Mutf8อย่างไรถ้าไม่อยู่ในหนึ่งซับ perl?
Lei Yang

@LeiYang:use utf8;
Dave Cross

80

ทั้งหมดuse utf8;นี้บอก Perl ว่าซอร์สโค้ดเข้ารหัสโดยใช้ UTF-8 คุณต้องบอก Perl ถึงวิธีเข้ารหัสข้อความของคุณ:

use open ':std', ':encoding(UTF-8)';

ขอบคุณสิ่งนี้ใช้งานได้ดีสำหรับโปรแกรมที่เก็บไว้ในไฟล์ซึ่งต่างจาก one-liners ในบรรทัดคำสั่งซึ่งคำตอบของ @ DaveCross ครอบคลุม
vktec

19

เข้ารหัสเอาต์พุตมาตรฐานทั้งหมดเป็น UTF-8:

binmode STDOUT, ":utf8";

2
use open ':std', ':encoding(UTF-8)';ตามที่เสนอโดยคำตอบอื่นทำสิ่งนี้สำหรับ STDOUT แต่ยังทำเครื่องหมาย STDERR และ STDIN เป็น UTF-8 ดังนั้นคุณจะได้รับสามในราคาเดียว ดูstackoverflow.com/a/42194059
Stephen Ostermiller

ตกลง. นี้จะดียิ่งขึ้น
Boris Ivanov

14

คุณจะได้รับใกล้เคียงกับ "เพียงแค่ทำทุก utf8" utf8::allโดยใช้โมดูล

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

เมื่อprintได้รับสิ่งที่ไม่สามารถพิมพ์ได้ (อักขระที่มีขนาดใหญ่กว่า 255 เมื่อไม่มี:encodingเลเยอร์) จะถือว่าคุณต้องการเข้ารหัสโดยใช้ UTF-8 หลังจากแจ้งเตือนเกี่ยวกับปัญหาแล้ว


5

คุณสามารถใช้สิ่งนี้

perl -CS filename.

นอกจากนี้ยังจะยุติข้อผิดพลาดนั้น


สิ่งนี้ช่วยได้เท่านั้น
muenalan

0

ในภาษาสเปนคุณจะพบข้อผิดพลาดนี้เมื่ออยู่ข้างๆเริ่มใช้:

use utf8;

การเข้ารหัสโปรแกรมแก้ไขของคุณอยู่ในการเข้ารหัสอื่น ดังนั้นสิ่งที่คุณเห็นในตัวแก้ไขไม่ใช่สิ่งที่ Perl ทำ เพื่อแก้ปัญหาที่ผิดพลาดเพียงแค่เปลี่ยนการเข้ารหัสแก้ไขเพื่อUnicode / UTF-8


1
ไม่นี่ไม่ใช่สาเหตุของข้อผิดพลาด รหัสทั้งหมดได้รับการเข้ารหัสอย่างถูกต้องเป็น UTF8 แต่ตัวจัดการไฟล์ที่ส่งออกไม่ทราบว่าเป็น
Dave Cross
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.