มีสองขั้นตอนในการประมวลผลข้อความ Unicode ที่แรกก็คือ "ฉันจะป้อนและส่งออกโดยไม่สูญเสียข้อมูล" ประการที่สองคือ "ฉันจะปฏิบัติต่อข้อความตามแบบแผนภาษาท้องถิ่นได้อย่างไร"
โพสต์ของ tchrist ครอบคลุมทั้งสอง แต่ส่วนที่สองคือตำแหน่งที่ 99% ของข้อความในโพสต์ของเขามาจาก โปรแกรมส่วนใหญ่ไม่ได้จัดการ I / O อย่างถูกต้องดังนั้นจึงเป็นสิ่งสำคัญที่จะต้องเข้าใจก่อนที่คุณจะเริ่มกังวลเกี่ยวกับการทำให้ปกติและการเรียงหน้า
โพสต์นี้มีวัตถุประสงค์เพื่อแก้ไขปัญหาแรก
เมื่อคุณอ่านข้อมูลไปยัง Perl มันไม่สนใจว่าจะเข้ารหัสอะไร มันจัดสรรหน่วยความจำบางส่วนและหยุดไบต์ที่นั่น ถ้าคุณบอกว่าprint $str
มันแค่แบ่งไบต์เหล่านั้นออกไปยังเทอร์มินัลของคุณซึ่งอาจถูกตั้งค่าให้ถือว่าทุกอย่างที่เขียนไว้คือ UTF-8 และข้อความของคุณจะปรากฏขึ้น
มหัศจรรย์
ยกเว้นมันไม่ใช่ หากคุณพยายามรักษาข้อมูลเป็นข้อความคุณจะเห็นว่ามีบางสิ่งไม่ดีเกิดขึ้น คุณไม่จำเป็นต้องไปไกลกว่านี้length
เพื่อดูว่า Perl คิดอย่างไรกับสตริงของคุณและสิ่งที่คุณคิดเกี่ยวกับสตริงไม่เห็นด้วย เขียนสิ่งที่คล้ายกัน: perl -E 'while(<>){ chomp; say length }'
และพิมพ์文字化け
และคุณจะได้ 12 ... ไม่ใช่คำตอบที่ถูกต้อง 4
นั่นเป็นเพราะ Perl ถือว่าสตริงของคุณไม่ใช่ข้อความ คุณต้องบอกว่ามันเป็นข้อความก่อนที่มันจะให้คำตอบที่ถูกต้อง
ง่ายพอ โมดูล Encode มีฟังก์ชันที่จะทำเช่นนั้น จุดเข้าใช้งานทั่วไปคือEncode::decode
(หรือuse Encode qw(decode)
แน่นอน) ฟังก์ชั่นนั้นใช้สตริงบางส่วนจากโลกภายนอก (สิ่งที่เราจะเรียกว่า "octets" วิธีแฟนซีของการพูดว่า "8-bit bytes") และเปลี่ยนเป็นข้อความที่ Perl จะเข้าใจ อาร์กิวเมนต์แรกคือชื่อการเข้ารหัสอักขระเช่น "UTF-8" หรือ "ASCII" หรือ "EUC-JP" อาร์กิวเมนต์ที่สองคือสตริง ค่าส่งคืนคือสเกลาร์ Perl ที่มีข้อความ
(นอกจากนี้ยังมีEncode::decode_utf8
ซึ่งสันนิษฐานว่าเป็น UTF-8 สำหรับการเข้ารหัส)
หากเราเขียนหนึ่งซับของเราใหม่:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
เราพิมพ์文字化けและรับ "4" เป็นผลลัพธ์ ความสำเร็จ
นั่นตรงนั้นเป็นวิธีแก้ปัญหา 99% ของปัญหา Unicode ใน Perl
ที่สำคัญคือเมื่อใดก็ตามที่ข้อความเข้ามาในโปรแกรมของคุณคุณจะต้องถอดรหัส อินเทอร์เน็ตไม่สามารถส่งอักขระได้ ไฟล์ไม่สามารถเก็บอักขระได้ ไม่มีตัวละครในฐานข้อมูลของคุณ มีออคเต็ตเท่านั้นและคุณไม่สามารถถือว่าอ็อคเท็ตเป็นตัวละครใน Perl คุณต้องถอดรหัสอ็อกเท็ตที่เข้ารหัสเป็นอักขระ Perl ด้วยโมดูลการเข้ารหัส
อีกครึ่งหนึ่งของปัญหาคือการนำข้อมูลออกจากโปรแกรมของคุณ นั่นเป็นเรื่องง่าย คุณเพียงแค่บอกว่าuse Encode qw(encode)
ตัดสินใจว่าการเข้ารหัสข้อมูลของคุณจะอยู่ใน (UTF-8 กับขั้วที่เข้าใจ UTF-8, UTF-16 สำหรับไฟล์บน Windows, ฯลฯ ) และแล้วออกผลมาจากการencode($encoding, $data)
แทนเพียง $data
outputting
การดำเนินการนี้จะแปลงอักขระของ Perl ซึ่งเป็นสิ่งที่โปรแกรมของคุณทำงานไปสู่อ็อกเท็ตที่สามารถใช้งานได้โดยโลกภายนอก มันจะง่ายกว่านี้มากถ้าเราสามารถส่งตัวละครผ่านทางอินเทอร์เน็ตหรือไปยังเครื่องเทอร์มินัลของเรา แต่เราทำไม่ได้: octets เท่านั้น ดังนั้นเราต้องแปลงอักขระเป็นอ็อกเท็ตมิฉะนั้นผลลัพธ์จะไม่ถูกกำหนด
ในการสรุป: เข้ารหัสเอาต์พุตทั้งหมดและถอดรหัสอินพุตทั้งหมด
ตอนนี้เราจะพูดถึงสามประเด็นที่ทำให้สิ่งนี้ท้าทายเล็กน้อย ที่แรกก็คือห้องสมุด พวกเขาจัดการข้อความอย่างถูกต้องหรือไม่ คำตอบคือ ... พวกเขาลอง หากคุณดาวน์โหลดเว็บเพจ LWP จะให้ผลลัพธ์เป็นข้อความ หากคุณเรียกใช้วิธีการที่ถูกต้องกับผลลัพธ์นั่นคือ (และสิ่งนั้นเกิดขึ้นdecoded_content
ไม่ใช่content
ซึ่งเป็นเพียงสตรีมออคเต็ตที่ได้มาจากเซิร์ฟเวอร์) ไดรเวอร์ฐานข้อมูลอาจไม่สม่ำเสมอ ถ้าคุณใช้ DBD :: SQLite ด้วย Perl เพียงอย่างเดียวมันจะได้ผล แต่ถ้ามีเครื่องมืออื่น ๆ ที่ใส่ข้อความที่เก็บไว้เป็นการเข้ารหัสแบบอื่นที่ไม่ใช่ UTF-8 ในฐานข้อมูลของคุณ ... ดี ... มันจะไม่ถูกจัดการอย่างถูกต้อง จนกว่าคุณจะเขียนโค้ดเพื่อจัดการอย่างถูกต้อง
ข้อมูลที่ส่งออกนั้นมักจะง่ายกว่า แต่ถ้าคุณเห็น "ตัวอักษรขนาดใหญ่ในการพิมพ์" คุณจะรู้ว่าคุณกำลังสับสนการเข้ารหัสอยู่ที่ไหนสักแห่ง คำเตือนนั้นหมายความว่า "เฮ้คุณกำลังพยายามรั่วไหลตัวละคร Perl ไปยังโลกภายนอกและนั่นก็ไม่สมเหตุสมผล" โปรแกรมของคุณดูเหมือนจะทำงานได้ (เพราะส่วนอื่น ๆ มักจะจัดการกับตัวอักขระ Perl ที่ถูกต้อง) แต่มันก็หักและสามารถหยุดทำงานได้ทุกเมื่อ แก้ไขด้วยความชัดเจนEncode::encode
!
ปัญหาที่สองคือซอร์สโค้ดที่เข้ารหัส UTF-8 เว้นแต่คุณจะพูดuse utf8
ที่ด้านบนของแต่ละไฟล์ Perl จะไม่คิดว่าซอร์สโค้ดของคุณคือ UTF-8 ซึ่งหมายความว่าทุกครั้งที่คุณพูดอะไรบางอย่างmy $var = 'ほげ'
คุณกำลังฉีดขยะเข้าไปในโปรแกรมซึ่งจะทำให้ทุกอย่างพังทลายอย่างสิ้นเชิง คุณไม่จำเป็นต้อง "ใช้ utf8" แต่ถ้าไม่คุณต้องไม่ใช้อักขระที่ไม่ใช่ ASCII ในโปรแกรมของคุณ
ปัญหาที่สามคือวิธีที่ Perl จัดการกับอดีต นานมาแล้วไม่มีสิ่งเช่น Unicode และ Perl คิดว่าทุกอย่างเป็นข้อความละตินหรือไบนารี ดังนั้นเมื่อข้อมูลเข้าสู่โปรแกรมของคุณและคุณเริ่มใช้มันเป็นข้อความ Perl จะถือว่าแต่ละ octet เป็นตัวอักษรละติน -1 นั่นเป็นเหตุผลที่เมื่อเราถามถึงความยาวของ "文字化け" เราได้ 12 Perl คิดว่าเรากำลังดำเนินการในสตริงละติน -1 "æååã" (ซึ่งคือ 12 ตัวอักษรบางส่วนที่ไม่ได้พิมพ์)
สิ่งนี้เรียกว่า "การอัพเกรดโดยนัย" และเป็นสิ่งที่สมเหตุสมผลอย่างสมบูรณ์แบบ แต่ไม่ใช่สิ่งที่คุณต้องการหากข้อความของคุณไม่ใช่ภาษาละติน -1 นั่นเป็นเหตุผลที่สำคัญที่จะต้องถอดรหัสอินพุตอย่างชัดเจน: หากคุณไม่ทำมัน Perl จะและอาจทำผิด
ผู้คนมีปัญหาในการที่ข้อมูลครึ่งหนึ่งของพวกเขาเป็นสตริงอักขระที่เหมาะสมและบางส่วนยังคงเป็นไบนารี Perl จะตีความส่วนที่ยังคงเป็นเลขฐานสองราวกับว่าเป็นข้อความแบบละติน -1 แล้วรวมเข้ากับข้อมูลอักขระที่ถูกต้อง สิ่งนี้จะทำให้ดูเหมือนว่าการจัดการตัวละครของคุณถูกทำลายโปรแกรมของคุณอย่างถูกต้อง แต่ในความเป็นจริงคุณไม่ได้แก้ไขให้เพียงพอ
ต่อไปนี้เป็นตัวอย่าง: คุณมีโปรแกรมที่อ่านไฟล์ข้อความที่เข้ารหัส UTF-8 คุณจะจับ Unicode PILE OF POO
ไปยังแต่ละบรรทัดแล้วพิมพ์ออกมา คุณเขียนมันชอบ:
while(<>){
chomp;
say "$_ 💩";
}
จากนั้นเรียกใช้ข้อมูลที่เข้ารหัส UTF-8 เช่น:
perl poo.pl input-data.txt
มันพิมพ์ข้อมูล UTF-8 ด้วย poo ที่ส่วนท้ายของแต่ละบรรทัด สมบูรณ์แบบโปรแกรมของฉันทำงาน!
แต่ไม่คุณกำลังทำการต่อข้อมูลไบนารี่ คุณกำลังอ่าน octets จากไฟล์ที่ลบ\n
กับ chomp แล้วตรึงบนไบต์ใน UTF-8 เป็นตัวแทนของPILE OF POO
ตัวละคร เมื่อคุณแก้ไขโปรแกรมของคุณเพื่อถอดรหัสข้อมูลจากไฟล์และเข้ารหัสผลลัพธ์คุณจะสังเกตเห็นว่าคุณได้รับขยะ ("ð©") แทนที่จะเป็น poo สิ่งนี้จะทำให้คุณเชื่อว่าการถอดรหัสไฟล์อินพุตเป็นสิ่งที่ผิดที่ต้องทำ มันไม่ใช่.
ปัญหาคือว่า poo กำลังถูกอัพเกรดโดยนัยเป็น latin-1 ถ้าคุณuse utf8
สร้างข้อความตามตัวอักษรแทนที่จะเป็นไบนารี่มันจะกลับมาทำงานอีกครั้ง!
(นั่นคือปัญหาอันดับหนึ่งที่ฉันเห็นเมื่อช่วยเหลือผู้คนที่มี Unicode พวกเขาทำในสิ่งที่ถูกต้องและทำลายโปรแกรมของพวกเขานั่นคือสิ่งที่น่าเศร้าเกี่ยวกับผลลัพธ์ที่ไม่ได้กำหนด: คุณสามารถมีโปรแกรมทำงานเป็นเวลานาน แต่เมื่อคุณเริ่มซ่อม มันหยุดไม่ต้องกังวลหากคุณเพิ่มคำสั่ง encode / decode ลงในโปรแกรมของคุณและหยุดพักมันก็หมายความว่าคุณมีงานที่ต้องทำอีกมากในครั้งต่อไปเมื่อคุณออกแบบด้วย Unicode ในใจตั้งแต่ต้นมันจะเป็น ง่ายกว่ามาก!)
นั่นคือทั้งหมดที่คุณต้องรู้เกี่ยวกับ Perl และ Unicode ถ้าคุณบอก Perl ว่าข้อมูลของคุณคืออะไรมันมีการสนับสนุน Unicode ที่ดีที่สุดในบรรดาภาษาโปรแกรมยอดนิยมทั้งหมด หากคุณสมมติว่ามันจะรู้ได้อย่างน่าอัศจรรย์ว่าข้อความประเภทใดที่คุณป้อนอยู่นั้นคุณจะทิ้งข้อมูลของคุณอย่างถาวร เพียงเพราะโปรแกรมของคุณทำงานในวันนี้ที่เทอร์มินัล UTF-8 ของคุณไม่ได้หมายความว่ามันจะทำงานในวันพรุ่งนี้ในไฟล์ที่เข้ารหัส UTF-16 ดังนั้นให้ปลอดภัยในตอนนี้และช่วยตัวคุณเองด้วยการกำจัดข้อมูลผู้ใช้ของคุณ!
ส่วนที่ง่ายของการจัดการ Unicode คือการเข้ารหัสเอาต์พุตและการถอดรหัสอินพุต ส่วนที่ยากคือการค้นหาอินพุตและเอาต์พุตทั้งหมดของคุณและพิจารณาว่าการเข้ารหัสนั้นคืออะไร แต่นั่นเป็นเหตุผลว่าทำไมคุณถึงได้เหรียญใหญ่ :)