Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

3 ขั้นตอนแก้ปัญหา Encoding ใน Ruby ที่พิสูจน์แล้ว

คุณคิดถึง encoding ของ string ก็ต่อเมื่อมันพัง เวลาดู exception tracker แล้วเห็น

Encoding::InvalidByteSequenceError: "\xFE" on UTF-8

จ้องอยู่หน้าตา หรือ "they’re" กลายเป็น "they’re"

ตอน encoding พัง จะหาสาเหตุและแก้ได้อย่างไร?

Encoding คืออะไร?

ถ้าคุณจินตนาการได้ว่า encoding ทำอะไรกับ string แบกนี้จะง่ายขึ้น

คิดว่าสตริงคืออาเรย์ของ bytes หรือตัวเลขเล็กๆ:

irb(main):001:0> "hello!".bytes => [104, 101, 108, 108, 111, 33]

ใน encoding นี้ 104 คือ h, 33 คือ ! ไปเรื่อยๆ

ซับซ้อนขึ้นกับตัวอักษรที่น้อยในภาษาอังกฤษ:

irb(main):002:0> "hellṏ!".bytes => [104, 101, 108, 108, 225, 185, 143, 33]

ตอนนี้ยากกว่าจะบอกว่าเลขไหนแทนตัวอักษรไหน แทนที่จะเป็น 1 byte ṏ แทนด้วยกลุ่ม bytes [225, 185, 143] แต่มันยังมีความสัมพันธ์ระหว่าง bytes และตัวอักษร และ encoding ของสตริงคือสิ่งที่กำหนดความสัมพันธ์นั้น

ดูชุด bytes เดียวกันตอนตีความด้วย encoding ต่างๆ:

# ลอง ISO-8859-1 string พร้อมตัวอักษรพิเศษ! irb(main):003:0> str = "hellÔ!".encode("ISO-8859-1"); str.encode("UTF-8") => "hellÔ!" irb(main):004:0> str.bytes => [104, 101, 108, 108, 212, 33] # ถ้าตีความเป็น ISO-8859-5 จะเป็นยังไง? irb(main):005:0> str.force_encoding("ISO-8859-5"); str.encode("UTF-8") => "hellд!" irb(main):006:0> str.bytes => [104, 101, 108, 108, 212, 33]

Bytes ไม่เปลี่ยน แตการเปลี่ยน encoding เปลี่ยนวิธีแสดงผลโดยไม่เปลี่ยน bytes

และไม่ใช่ทุกสตริงแทนได้ในทุก encoding:

irb(main):006:0> "hi∑".encode("Windows-1252") Encoding::UndefinedConversionError: U+2211 to WINDOWS-1252 in conversion from UTF-8 to WINDOWS-1252 from (irb):61:in `encode' from (irb):61 from /usr/local/bin/irb:11:in `<main>'

Encoding ส่วนใหญ่เล็ก ไม่รองรับทุกตัวอักษร จะเจอ error ตอนตัวอักษรใน encoding หนึ่งไม่มีในอีกอัน หรือ Ruby แปลงไม่ได้

หลีกเลี่ยง error นี้ได้ด้วย options ใน encode:

irb(main):064:0> "hi∑".encode("Windows-1252", invalid: :replace, undef: :replace) => "hi?"

invalid และ undef แทนที่ตัวอักษรที่แปลงไม่ได้ด้วยตัวอักษรอื่น ค่าเริ่มต้นคือ ? (ตอนแปลงเป็น Unicode คือ )

แตตอนแทนที่ด้วย encode อาจสูญเสียข้อมูล ไม่รู้ว่า bytes ไหนถูกแทนที่ด้วย ? แตถ้าจำเป็นต้องอยู่ใน encoding ใหม่ การสูญเสียข้อมูลดีกว่าการพัง

จนตอนนี้เห็น 3 เมธอดสำคัญ:

  • encode - แปลงสตริงเป็น encoding อื่น (แปลงตัวอักษรเป็นเทียบเท่าใน encoding ใหม่)
  • bytes - แสดง bytes ที่ประกอบสตริง
  • force_encoding - แสดงว่า bytes นั้นจะเห็นยังไงถ้าตีความด้วย encoding อื่น

ความต่างหลัก: encode อาจเปลี่ยน bytes แต่ force_encoding ไม่เปลี่ยน

กระบวนการ 3 ขั้นตอนแก้ Encoding Bugs

แก้ปัญหา encoding ส่วนใหญ่ได้ด้วย 3 ขั้นตอน:

1. ค้นหา Encoding จริงของสตริง

ดูง่าย แตแค่สตริง บอก ว่าเป็น encoding ไหน ไม่ได้หมายความว่ามันจริง:

irb(main):078:0> "hi\x99!".encoding => #<Encoding:UTF-8>

ไม่ถูกต้อง - ถ้าเป็น UTF-8 จริงๆ จะไม่มี backslashed number แปลกๆ อยู่ ดังนั้นหา encoding ที่ถูกต้องอย่างไร?

ซอฟต์แวร์เก่ามักใช้ default encoding เดียว จึงค้นหาได้จากแหล่งที่มา มาจาก Word? อาจเป็น Windows-1252 มาจากไฟล์หรือเว็บเก่า? อาจเป็น ISO-8859-1

การค้นหาตาราง encoding เช่นใน Wikipedia ก็ช่วย ดูตัวอักษรที่ bytes ไม่รู้จักแทน ดูว่าเข้าใจในบริบทไหม

ตัวอย่างนี้ Windows-1252 chart บอกว่า byte 99 คือ "™" Byte 99 ไม่มีใน ISO-8859-1 ถ้า ™ เข้าใจได้ในบริบท สมมติว่า input เป็น Windows-1252 ได้ ไม่เช่นนั้นค้นต่อจนเจอตัวอักษรที่สมเหตุสมผลกว่า

2. ตัดสินใจ Encoding ที่ต้องการ

เรื่องนี้ง่าย ถ้าไม่มีเหตุผลพิเศษ คุณต้องการ string เป็น UTF-8

อีก encoding หนึ่งที่พบบ่อยใน Ruby: ASCII-8BIT ใน ASCII-8BIT ทุกตัวอักษรแทนด้วย 1 byte คือ str.chars.length == str.bytes.length ถ้าอยากควบคุม bytes โดยตรง ASCII-8BIT อาจดี

3. แปลง Encoding ใหม่จากขั้นตอน 1 ไปขั้นตอน 2

ใช้ encode ตัวอย่างนี้ string เป็น Windows-1252 และเรา อยากให้ เป็น UTF-8:

irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252") => "hi™!"

ดีขึ้นเยอะ (แม้ลำดับ encoding ในการเรียกจะดูถอยหลัง)

การจินตนาการการตีความ bytes เดียวกันหลายแบบทำปวดหัว โดยเฉพาะ ตอนหนึ่งในนั้นพัง แตมีวิธีทำให้คล่องกับ encodings: เล่นกับมัน

เปิด irb ลอง encode, bytes, force_encoding ดูว่า encode เปลี่ยน bytes ยังไง สร้าง直觉เกี่ยวกับ encoding ต่างๆ เมื่อคล่องขึ้นและใช้ขั้นตอนนี้ คุณจะแก้ในนาทีสิ่งที่เคยใช้ชั่วโมง

สุดท้าย ถ้าอยากเรียนรู้วิธีทำให้การเรียนรู้แบบนี้กลายเป็นนิสัย ดาวน์โหลดบทตัวอย่างฟรีของหนังสือผม การทำลายสิ่งต่างๆ ในคอนโซลเป็นวิธีเรียนรู้ที่สนุกมาก