คุณลักษณะใหม่ที่มาพร้อมกับ Ruby 2.4 นั้นได้รับการปรับปรุงการรองรับ Unicode โดยเฉพาะวิธีการเช่น upcase
และ downcase
ทำงานตามที่คาดไว้โดยเปลี่ยน "ä" เป็น "Ä" แล้วย้อนกลับ สิ่งนี้ทำให้ฉันสงสัย:มีการปรับปรุง Unicode อื่นใดอีกบ้างตั้งแต่ปี 2013 เมื่อฉันอ่านบล็อกโพสต์บล็อกของ André Arko ใน Ruby เป็น UTF-8 ตอนนี้…ใช่ไหม
ฉันทดสอบวิธีการสตริงของ Ruby ทั้งหมด ไม่ได้มองหาข้อผิดพลาดทางเทคนิคแต่สำหรับการละเมิด "หลักการที่น่าประหลาดใจน้อยที่สุด" โดยเฉพาะอย่างยิ่ง สมมติฐานของฉันคือ:
- อักขระที่ไม่ซ้ำกันมีเอกลักษณ์เฉพาะ: "e" และ "ë" ต่างกัน เช่นเดียวกับ "e" และ "E"
- อักขระตัวเดียวนับเป็นอักขระตัวเดียว ไม่ว่าพวกมันจะแสดงเป็นยูนิโค้ดอย่างไร ซึ่งหมายความว่า "e" และ "ë" แต่ละตัวเป็นอักขระตัวเดียว แม้ว่าตัวหลังจะแสดงด้วยจุดโค้ดสองจุด
- ตัวละครไม่สามารถเปลี่ยนได้ การย้อนกลับสตริงของอักขระไม่ควรเปลี่ยนอักขระแต่ละตัว
- ช่องว่างถือเป็นช่องว่าง แม้แต่อักขระช่องว่าง Unicode ที่ยุ่งยากเหล่านั้น
- ตัวเลขถือเป็นตัวเลข เลข 2 จะเป็นเลข 2 เสมอไม่ว่าจะเขียนอย่างไร
น่าเสียดายที่วิธีจัดการสตริงของ Ruby ส่วนใหญ่ไม่ผ่านการทดสอบเหล่านี้ หากคุณกำลังทำงานกับสตริง Unicode คุณจึงต้องระมัดระวังเป็นอย่างยิ่งว่าจะใช้สตริงใด
หมายเหตุ:หลังจากการตีพิมพ์ ผู้อ่านบางคนชี้ให้เห็นว่าความล้มเหลวหลายอย่างที่ฉันพูดถึงจะไม่เกิดขึ้นถ้าฉันจะทำให้สตริงการทดสอบ Unicode เป็นมาตรฐาน นี่เป็นเรื่องจริง อย่างไรก็ตาม Ruby หรือ Rails จะไม่ทำให้สตริงเป็นมาตรฐานโดยอัตโนมัติ (ในแอปใดๆ ที่ฉันทดสอบ) การทดสอบเหล่านี้มีขึ้นเพื่อแสดงกรณีที่เลวร้ายที่สุดเสมอ และฉันคิดว่าการทดสอบเหล่านี้ยังคงมีประโยชน์ในเรื่องนั้น
ทดสอบ Unicode ด้วย Ruby 2.4.0
วิธีการ | ทดสอบ | คาดว่า | ผลลัพธ์ | คำพิพากษา |
---|---|---|---|---|
#% | "%s" % "noël" | "noël" | "noël" | ตกลง |
#* | "noël" * 2 | "noëlnoël" | "noëlnoël" | ตกลง |
#<< | "noël" << "ë" | "noël" | "noël" | ตกลง |
#<=> | "ä" <=> "z" | -1 | -1 | ตกลง |
#== | "ä" == "ä" | true | true | ตกลง |
#=~ | "ä" =~ /a./ | nil | 0 | ระวัง! |
#[] | "ä"[0] | "ä" | "a" | ระวัง! |
#[]= | "ä"[0] = "u" | "u" | "u" | ตกลง |
#b | "ä".b.encoding.to_s | "ASCII-8BIT" | "ASCII-8BIT" | ตกลง |
#bytes | "ä".bytes | [97, 204, 136] | [97, 204, 136] | ตกลง |
#bytesize | "ä".bytesize | 3 | 3 | ตกลง |
#byteslice | "ä".byteslice(1) | "\xCC" | "\xCC" | ตกลง |
#capitalize | "ä".capitalize | "Ä" | "Ä" | ตกลง |
#casecmp | "äa".casecmp("äz") | -1 | -1 | ตกลง |
#center | "ä".center(3) | " ä " | "ä " | ระวัง! |
#chars | "ä".chars | ["ä"] | ["a", "̈"] | ระวัง! |
#chomp | "ä
".chomp | "ä" | "ä" | ตกลง |
#chop | "ä".chop | "̈" | "a" | ระวัง! |
#chr | "ä".chr | "ä" | "a" | ระวัง! |
#เคลียร์ | "ä".clear | "̈" | "̈" | ตกลง |
#codepoints | "ä".codepoints | [97, 776] | [97, 776] | ตกลง |
#concat | "ä".concat("x") | "äx" | "äx" | ตกลง |
#count | "ä".count("a") | 0 | 1 | ระวัง! |
#crypt | "123".crypt("ää") == "123".crypt("aa") | false | false | ตกลง |
#delete | "ä".delete("a") | "ä" | "̈" | ระวัง! |
#downcase | "Ä".downcase | "ä" | "ä" | ตกลง |
#dump | "ä".dump | "\"a\\u0308\"" | "\"a\\u0308\"" | ตกลง |
#each_byte | "ä".each_byte.to_a | [97, 204, 136] | [97, 204, 136] | ตกลง |
#each_char | "ä".each_char.to_a | ["ä"] | ["a", "̈"] | ระวัง! |
#each_codepoint | "ä".each_codepoint.to_a | [97, 776] | [97, 776] | ตกลง |
#each_line | "ä".each_line.to_a | ["ä"] | ["ä"] | ตกลง |
#ว่างไหม | "ä".empty? | false | false | ตกลง |
#encode | "ä".encode("ASCII", undef: :replace) | "a?" | "a?" | ตกลง |
#encoding | "ä".encoding.to_s | "UTF-8" | "UTF-8" | ตกลง |
#end_with? | "ä".end_with?("ä") | true | true | ตกลง |
#eql? | "ä".eql?("a") | false | false | ตกลง |
#force_encoding | "ä".force_encoding("ASCII") | "a\xCC\x88" | "a\xCC\x88" | ตกลง |
#getbyte | "ä".getbyte(2) | 136 | 136 | ตกลง |
#gsub | "ä".gsub("a", "x") | "ä" | "ẍ" | ระวัง! |
#แฮช | "ä".hash == "a".hash | false | false | ตกลง |
#include? | "ä".include?("a") | false | true | ระวัง! |
#index | "ä".index("a") | nil | 0 | ระวัง! |
#replace | "ä".replace("u") | "u" | "u" | ตกลง |
#insert | "ä".insert(1, "u") | "äu" | "aü" | ระวัง! |
#ตรวจสอบ | "ä".inspect | "\"ä\"" | "\"ä\"" | ตกลง |
#ฝึกงาน | "ä".intern | :ä | :ä | ตกลง |
#length | "ä".length | 1 | 2 | ระวัง! |
#ljust | "ä".ljust(3, "_") | "ä__" | "ä_" | ระวัง! |
#lstrip | " ä".lstrip | "ä" | "ä" | ตกลง |
#match | "ä".match("a") | nil | # | ระวัง! |
#ต่อไป | "ä".next | "ä" | "b̈" | ระวัง! |
#ord | "ä".ord | 97 | 97 | ตกลง |
#พาร์ทิชัน | "händ".partition("a") | ["händ"] | ["h", "a", "̈nd"] | ระวัง! |
#prepend | "ä".prepend("ä") | "ää" | "ää" | ตกลง |
#replace | "ä".replace("ẍ") | "ẍ" | "ẍ" | ตกลง |
#ย้อนกลับ | "händ".reverse | "dnäh" | "dn̈ah" | ระวัง! |
#rpartition | "händ".rpartition("a") | ["händ"] | ["h", "a", "̈nd"] | ระวัง! |
#rstrip | "line ".rstrip | "line" | "line " | ระวัง! |
#scrub | "ä".scrub | "ä" | "ä" | ตกลง |
#setbyte | s = "ä"; s.setbyte(0, "x".ord); s | "ẍ" | "ẍ" | ตกลง |
#ขนาด | "ä".size | 1 | 2 | ระวัง! |
#slice | "ä".slice(0) | "ä" | "a" | ระวัง! |
#split | "ä".split("a") | ["ä"] | ["", "̈"] | ระวัง! |
#บีบ | "ää".squeeze("ä") | "ä" | "ää" | ระวัง! |
#start_with? | "ä".start_with?("a") | false | true | ระวัง! |
#สตริป | " line ".strip | "line" | " line " | ระวัง! |
#sub | "ä".sub("a", "x") | "ä" | "ẍ" | ระวัง! |
#succ | "ä".succ | "b̈" | "b̈" | ตกลง |
#swapcase | "ä".swapcase | "Ä" | "Ä" | ตกลง |
#to_c | "١".to_c | (1+0i) | (0+0i) | ระวัง! |
#to_f | "١".to_f | 1.0 | 0.0 | ระวัง! |
#to_i | "١".to_i | 1 | 0 | ระวัง! |
#to_r | "١".to_r | (1/1) | (0/1) | ระวัง! |
#to_sym | "ä".to_sym | :ä | :ä | ตกลง |
#tr | "ä".tr("a", "b") | "ä" | "b̈" | ระวัง! |
#unpack | "ä".unpack("CCC") | [97, 204, 136] | [97, 204, 136] | ตกลง |
#ไม่เกิน | "ä".upto("c̈").to_a | ["ä", "b̈", "c̈"] | ["ä", "b̈", "c̈"] | ตกลง |
#valid_encoding? | "ä".valid_encoding? | true | true | ตกลง |