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

3 ขั้นตอนในการแก้ไขปัญหาการเข้ารหัสใน Ruby

คุณคิดจริงๆ เกี่ยวกับการเข้ารหัสของสตริงเมื่อมันขาด เมื่อคุณตรวจสอบตัวติดตามข้อยกเว้นแล้วดู

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 เปลี่ยนไบต์ที่ประกอบเป็นสตริง สร้างสัญชาตญาณว่าการเข้ารหัสต่างกันอย่างไร เมื่อคุณเริ่มคุ้นเคยกับการเข้ารหัสและใช้ขั้นตอนเหล่านี้แล้ว คุณจะแก้ไขสิ่งที่อาจต้องใช้เวลาหลายชั่วโมงก่อนหน้านี้ได้ในไม่กี่นาที

สุดท้ายนี้ หากคุณต้องการเรียนรู้วิธีสร้างนิสัยจากการเรียนรู้สิ่งเหล่านี้ด้วยการทำ ให้คว้าบทตัวอย่างฟรีในหนังสือของฉัน การทำลายสิ่งต่าง ๆ ในคอนโซลเป็น จริงๆ วิธีสนุกๆ ในการศึกษาไอเดียแบบนี้