คุณคิดจริงๆ เกี่ยวกับการเข้ารหัสของสตริงเมื่อมันขาด เมื่อคุณตรวจสอบตัวติดตามข้อยกเว้นแล้วดู
Encoding::InvalidByteSequenceError: "\xFE" on UTF-8
จ้องมองคุณในใบหน้า หรือบางที “พวกเขากำลัง” เริ่มปรากฏเป็น “พวกเขากำลัง”
ดังนั้น เมื่อคุณมีการเข้ารหัสที่ไม่ดี คุณจะทราบได้อย่างไรว่าสิ่งใดมีปัญหา แล้วจะแก้ไขได้อย่างไร
การเข้ารหัสคืออะไร
หากคุณนึกภาพออกว่าการเข้ารหัสทำอะไรกับสตริง ข้อบกพร่องเหล่านี้แก้ไขได้ง่ายกว่า
คุณสามารถคิดว่าสตริงเป็นอาร์เรย์ไบต์หรือตัวเลขขนาดเล็กได้:
irb(main):001:0> "hello!".bytes
=> [104, 101, 108, 108, 111, 33]
ในการเข้ารหัสนี้ 104
หมายถึง h
, 33
หมายถึง !
, และอื่นๆ
มันจะยากขึ้นเมื่อคุณใช้อักขระที่ไม่ค่อยพบในภาษาอังกฤษ:
irb(main):002:0> "hellṏ!".bytes
=> [104, 101, 108, 108, 225, 185, 143, 33]
ตอนนี้เป็นการยากที่จะบอกว่าตัวเลขใดแสดงถึงอักขระใด แทนที่จะเป็นหนึ่งไบต์ ṏ
แสดงโดยกลุ่มของไบต์ [225, 185, 143]
. แต่ยังคงมีความสัมพันธ์ระหว่างไบต์และอักขระ และการเข้ารหัสของสตริงจะกำหนดความสัมพันธ์นั้น
ดูลักษณะของไบต์ชุดเดียวเมื่อคุณลองใช้การเข้ารหัสแบบอื่น:
# Try an ISO-8859-1 string with a special character!
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]
# What would that string look like interpreted as ISO-8859-5 instead?
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]
ไบต์ไม่เปลี่ยนแปลง แต่นั่นดูไม่ถูกต้องเลย การเปลี่ยนการเข้ารหัสเปลี่ยนวิธีพิมพ์สตริงโดยไม่เปลี่ยนไบต์
และไม่สามารถแสดงสตริงทั้งหมดได้ในการเข้ารหัสทั้งหมด :
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>'
การเข้ารหัสส่วนใหญ่มีขนาดเล็ก และไม่สามารถจัดการกับทุกตัวอักษรที่เป็นไปได้ คุณจะเห็นข้อผิดพลาดนั้นเมื่ออักขระในการเข้ารหัสหนึ่งไม่มีอยู่ในอีกการเข้ารหัสหนึ่ง หรือเมื่อ Ruby ไม่ทราบวิธีแปลอักขระระหว่างการเข้ารหัสทั้งสองแบบ
คุณสามารถแก้ไขข้อผิดพลาดนี้ได้ หากคุณส่งตัวเลือกเพิ่มเติมไปยัง encode
:
irb(main):064:0> "hi∑".encode("Windows-1252", invalid: :replace, undef: :replace)
=> "hi?"
invalid
และ undef
ตัวเลือกแทนที่อักขระที่ไม่สามารถแปลด้วยอักขระอื่นได้ โดยค่าเริ่มต้น อักขระแทนที่นั้นคือ ?
. (เมื่อคุณแปลงเป็น Unicode จะเป็น �)
ขออภัย เมื่อคุณแทนที่อักขระด้วย encode
คุณอาจสูญเสียข้อมูล คุณไม่รู้หรอกว่าไบต์ใดถูกแทนที่ด้วย ?
. แต่ถ้าคุณต้องการให้ข้อมูลของคุณอยู่ในการเข้ารหัสใหม่ การสูญเสียข้อมูลอาจดีกว่าสิ่งที่ถูกทำลาย
จนถึงตอนนี้ คุณได้เห็นวิธีสตริงหลักสามวิธีเพื่อช่วยให้คุณเข้าใจการเข้ารหัส:
-
encode
ซึ่งแปลสตริงเป็นการเข้ารหัสอื่น (แปลงอักขระให้เทียบเท่าในการเข้ารหัสใหม่) -
bytes
ซึ่งจะแสดงให้คุณเห็นไบต์ที่ประกอบเป็นสตริง -
force_encoding
ซึ่งจะแสดงให้คุณเห็นว่าไบต์เหล่านั้นจะมีลักษณะอย่างไรเมื่อถูกเข้ารหัสโดยการเข้ารหัสที่แตกต่างกัน
ความแตกต่างที่สำคัญระหว่าง encode
และ force_encoding
คือ encode
อาจเปลี่ยน bytes
และ force_encoding
จะไม่
กระบวนการสามขั้นตอนสำหรับการแก้ไขข้อผิดพลาดในการเข้ารหัส
คุณแก้ไขปัญหาการเข้ารหัสส่วนใหญ่ได้ด้วย 3 ขั้นตอน:
1. ค้นหาว่าการเข้ารหัสสตริงใดจริงๆ แล้ว ใน.
ฟังดูง่าย แต่เพียงเพราะสตริง พูด เป็นการเข้ารหัสบางอย่าง ไม่ได้หมายความว่าจะเป็น:
irb(main):078:0> "hi\x99!".encoding
=> #<Encoding:UTF-8>
ไม่ถูกต้อง – ถ้าเป็น จริงๆ UTF-8 จะไม่มีตัวเลขแบ็กสแลชแปลก ๆ อยู่ในนั้น คุณจะหาการเข้ารหัสที่เหมาะสมสำหรับสตริงของคุณได้อย่างไร
ซอฟต์แวร์รุ่นเก่าจำนวนมากจะใช้การเข้ารหัสเริ่มต้นเพียงตัวเดียว ดังนั้นคุณจึงสามารถค้นคว้าได้ว่าอินพุตมาจากไหน มีคนวางจาก Word หรือไม่ อาจเป็น Windows-1252 มาจากไฟล์หรือคุณดึงมาจากเว็บไซต์ที่เก่ากว่า อาจเป็น ISO-8859-1
นอกจากนี้ ฉันยังพบว่าการค้นหาตารางการเข้ารหัสมีประโยชน์ เช่นเดียวกับตารางในหน้า Wikipedia ที่เชื่อมโยงเหล่านั้น ในตารางเหล่านั้น คุณสามารถค้นหาอักขระที่อ้างอิงโดยตัวเลขที่ไม่รู้จัก และดูว่ามีความหมายในบริบทหรือไม่
ในตัวอย่างนี้ แผนภูมิ Windows-1252 แสดงว่าไบต์ 99
แสดงถึงอักขระ “™” ไบต์ 99
ไม่มีอยู่ภายใต้ ISO-8859-1 ถ้า ™ เข้าท่า คุณอาจถือว่าอินพุตอยู่ใน Windows-1252 แล้วไปต่อ มิฉะนั้น คุณสามารถค้นคว้าต่อไปได้จนกว่าจะพบตัวละครที่ดูสมเหตุสมผลกว่า
2. เลือกการเข้ารหัสที่คุณต้องการต้องการ สตริงที่จะเป็น
อันนี้ง่าย เว้นแต่คุณจะมีเหตุผลที่ดีจริงๆ คุณต้องการให้สตริงของคุณเข้ารหัสแบบ UTF-8
มีการเข้ารหัสทั่วไปอีกอย่างหนึ่งที่คุณอาจใช้ใน Ruby:ASCII-8BIT ใน ASCII-8BIT อักขระทุกตัวจะแสดงด้วยไบต์เดียว นั่นคือ str.chars.length == str.bytes.length
. ดังนั้น หากคุณต้องการควบคุมไบต์เฉพาะในสตริงของคุณเป็นจำนวนมาก ASCII-8BIT อาจเป็นตัวเลือกที่ดี
3. เข้ารหัสสตริงของคุณอีกครั้งจากการเข้ารหัสในขั้นตอนที่ 1 เป็นการเข้ารหัสในขั้นตอนที่ 2
คุณสามารถทำได้ด้วย encode
กระบวนการ. ในตัวอย่างนี้ สตริงของเรา คือ ในการเข้ารหัส Windows-1252 และเรา ต้องการ มันจะกลายเป็น UTF-8 ค่อนข้างตรงไปตรงมา:
irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252")
=> "hi™!"
ดีขึ้นมาก (แม้ว่าลำดับของการเข้ารหัสในการโทรนั้นจะดูย้อนหลังสำหรับฉันเสมอ)
การจินตนาการถึงการตีความที่แตกต่างกันของอาร์เรย์ไบต์เดียวกันอาจทำให้สมองต้องก้มหน้า โดยเฉพาะ เมื่อการตีความอย่างใดอย่างหนึ่งขาดหายไป แต่มีวิธีที่ยอดเยี่ยมในการทำให้คุ้นเคยกับการเข้ารหัสมากขึ้น:เล่นกับพวกเขา
เปิด irb
คอนโซลและยุ่งกับ encode
, bytes
และ force_encoding
. ดูวิธีการencode
เปลี่ยนไบต์ที่ประกอบเป็นสตริง สร้างสัญชาตญาณว่าการเข้ารหัสต่างกันอย่างไร เมื่อคุณเริ่มคุ้นเคยกับการเข้ารหัสและใช้ขั้นตอนเหล่านี้แล้ว คุณจะแก้ไขสิ่งที่อาจต้องใช้เวลาหลายชั่วโมงก่อนหน้านี้ได้ในไม่กี่นาที
สุดท้ายนี้ หากคุณต้องการเรียนรู้วิธีสร้างนิสัยจากการเรียนรู้สิ่งเหล่านี้ด้วยการทำ ให้คว้าบทตัวอย่างฟรีในหนังสือของฉัน การทำลายสิ่งต่าง ๆ ในคอนโซลเป็น จริงๆ วิธีสนุกๆ ในการศึกษาไอเดียแบบนี้