Ruby :: Representing Unprintable Characters
Posted by PunNeng, Tue Mar 06 03:00:00 UTC 2007
ถ้าต้องการจะจัดการกับ string หรือที่เป็นชนิด utf-8 หรือที่เป็นอักขระที่ไม่ได้อยู่บน keyboard จะทำยังไง มาดูกัน
เราสามารถจัดการกับอักขระได้ โดย ruby จะมีตัวเลขที่ผ่านการ escape มาแล้วเป็นตัวอ้าง โดยที่เราสามารถใช้ได้ใน double quote จะเพิ่มอะไรไปก็ได้ สามารถอ้างถึงอักขระตัวใดก็ได้โดยการ encode ให้เป็นเลขฐาน 8 ในรูปแบบนี้ "\000" หรือฐาน 16 ในรูปแบบนี้ "\x00"
1 2 3 4 5 6 7 8 9 10 11 12 13 | octal = "\000\001\010\020" octal.each_byte { |x| puts x } # 0 # 1 # 8 # 16 hexadecimal = "\x00\x01\x10\x20" hexadecimal.each_byte { |x| puts x } # 0 # 1 # 16 # 32 |
ด้วยกลไกแบบนี้ เลยสามารถทำให้ ruby แสดงอักขระ utf-8 ที่เราไม่สามารถพิมพ์มันออกมาได้ ลองรัน code นี้ดู มันจะทำการสร้าง file ที่ชื่อว่า smiley.html มาให้ดู
1 2 3 4 | open('smiley.html', 'wb') do |f| f << '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">' f << "\xe2\x98\xBA" end |
ตัวยิ้มนี้ มันมีอยู่ตรงไหนบนแป้นพิมพ์ ???
อักขระจำพวกนี้ จะมี back slash นำหน้า เช่น
1 2 3 4 5 6 7 8 | "\a" == "\x07" # => true # ASCII 0x07 = BEL (Sound system bell) "\b" == "\x08" # => true # ASCII 0x08 = BS (Backspace) "\e" == "\x1b" # => true # ASCII 0x1B = ESC (Escape) "\f" == "\x0c" # => true # ASCII 0x0C = FF (Form feed) "\n" == "\x0a" # => true # ASCII 0x0A = LF (Newline/line feed) "\r" == "\x0d" # => true # ASCII 0x0D = CR (Carriage return) "\t" == "\x09" # => true # ASCII 0x09 = HT (Tab/horizontal tab) "\v" == "\x0b" # => true # ASCII 0x0B = VT (Vertical tab) |
ตัว ruby มันเก็บ string เป้นลำดับของ byte ดังนั้น การที่มันจะเอา byte ต่างๆ ที่สามารถเอาออกมาให้ดูได้ เช่น ASCII, binary หรือปนๆ กัน มันก็ย่อมจะทำได้โดยไม่มีปัญหา
ตอนที่ ruby ได้แสดงอักขระต่างๆ ที่เราได้เห็น มันจะใช้อักขระ \xxx (ฐาน 8 ) เป็นตัวแทนของอักขระนั้นๆ ถ้าอักขระจำพวกข้างบนที่เพิ่งแสดงให้ดูไป มันก็จะออกมาให้ดูด้วย แต่ถ้าหากว่ามันไม่มีตัวแทนของอักขระนั้นๆ อยู่ มันก็จะแสดงออกมาในรูปฐาน 10 เช่น
1 2 | "\x10\x11\xfe\xff" # => "\020\021\376\377" "\x48\145\x6c\x6c\157\x0a" # => "Hello\n" |
เจอ backslash แล้วก็งง ถ้าเราจะแสดงตัว backslash จะใช้ backslash นำหน้าอีกทีนึง เช่น
1 2 3 4 5 | "\\".size # => 1 "\\" == "\x5c" # => true "\\n"[0] == ?\\ # => true "\\n"[1] == ?n # => true "\\n" =~ /\n/ # => nil |
แม้กระทั้ง shortcuts ต่างๆ ของ keyboard ก็แสดงได้ โดยจะอยู่ในรูป \C-x สำหรับการกด Ctrl+x หรือ \M-x สำหรับการกด Alt+x
1 2 | "\C-a\C-b\C-c" # => "\001\002\003" "\M-a\M-b\M-c" # => "\341\342\343" |
ถ้าเราใช้ ? นำหน้าอักขระต่างๆ เราจะได้ตัวเลขมาค่านึง(ขอเรียกว่า shorthand) ซึ่งสามารถใช้ได้กับ regular expression อีกด้วย
1 2 3 4 5 6 7 8 | ?\C-a # => 1 ?\M-z # => 250 contains_control_chars = /[\C-a-\C-^]/ 'Foobar' =~ contains_control_chars # => nil "Foo\C-zbar" =~ contains_control_chars # => 3 contains_upper_chars = /[\x80-\xff]/ 'Foobar' =~ contains_upper_chars # => nil "Foo\212bar" =~ contains_upper_chars # => 3 |
ลองดููตัวอย่าง
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def snoop_on_keylog(input) input.each_byte do |b| case b when ?\C-c; puts 'Control-C: stopped a process?' when ?\C-z; puts 'Control-Z: suspended a process?' when ?\n; puts 'Newline.' when ?\M-x; puts 'Meta-x: using Emacs?' end end end snoop_on_keylog("ls -ltR\003emacsHello\012\370rot13-other-window\012\032") # Control-C: stopped a process? # Newline. # Meta-x: using Emacs? # Newline. # Control-Z: suspended a process? |
พอได้รับ input มาแล้ว ก็ไล่ตรวจทีละ byte พอเข้า case ไหน ก็แสดงใน case นั้น
ตัวอักขระพิเศษพวกนี้ จะสามารถแสดงได้ใน double quote เท่านั้น ซึ่งเคยย้ำไปแล้วในเรื่องเล่าอันก่อนๆ มาย้ำอีกที(ย้ำตัวเองด้วย) สำหรับ double quote นอกจากตัวฟันหนูสองซี่แล้ว ยังสามารถใช้ %{} หรือ %Q{} ได้อีกด้วย แต่สำหรับ single quote หรือฟันหนูซี่เดียว สามารถใช้ %q{} แทน ส่วน here document นั้น จะให้ผลเป็น double quote ครับ
แก้ไขล่าสุด วันที่ 3 สิงหาคม 2550 เวลา 3.09 น.