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 น.

Filed Under: Ruby | Tags: howto ruby string

Comments

Have your say

A name is required. You may use HTML in your comments.




codegent: we're hiring