Displaying articles with tag string

Ruby :: Converting between Strings and Symbols

Posted by PunNeng, Tue May 22 03:10:00 UTC 2007

เล่นกับ symbol หน่อย อยากจะใช้ symbol เป็นตัวระบุอะไรบางอย่าง ใน ruby จะทำไง มาเล่นกันหน่อย

ทำได้ไม่ยาก เพียงแต่ใช้ Symbol#to_s หรือ Symbol#id2name ก็จะแปลง symbol ไปเป็น String หรือแปลงกลับ โดยการใช้ String#to_sym หรือ String.intern ตัวอย่าง

  1
  2
  3
  4
:symbol.to_s             # => "symbol"
:symbol.id2name          # => "symbol"
"symbol".to_sym          # => :symbol
"symbol".intern          # => :symbol

ข้อดีของ symbol อยู่ตรงที่มันไม่เปลืองหน่วยความจำและประหยัดเวลา ลองนี่ดู

  1
  2
  3
  4
"symbol".object_id       # => 23211108
"symbol".object_id       # => 23207142
:symbol.object_id        # => 3041550
:symbol.object_id        # => 3041550

ใช้ object_id ในการเรียกค่าอ้างอิง เราจะได้ค่าอ้างอิงจาก string สองค่า ที่ไม่ซ้ำกัน นั่นหมายความว่า มันย้ายหน่วยความจำไปอีกที่นึง และเราจะได้ค่าอ้างอิงจาก symbol เพียงค่าเดียว ประหยัดทั้งหน่วยความจำ และเวลา

แล้วเราจะใช้ string ทำอะไรล่ะ ==' เมื่อมี symbol แล้ว แน่นอน ว่า string ทำอะไรได้เยอะกว่า symbol แน่นอน แล้วจะดูยังไงว่าเมื่อไหร่ จะใช้ symbol หรือ string ?? ง่ายๆ เลย

  • ถ้าข้อมูลสำคัญ ใช้ string
  • ถ้าต้องการระบุอะไรบางอย่าง ก็ใช้ symbol

ทีนี้ flag ต่างๆ นานา ที่เราแค่ทำบันทึกค่าไว้ อาจจะเอามาแค่เปรียบเทียบกัน ก็ใช้ symbol เอาดีกว่า

  1
  2
  3
flag1 = :complete
flag2 = :complete
puts "done" if flag1 == flag2      # => done

เย่ๆ

ปล. ชาว Flash เตรียมตัวเมามันกับ ActionScript 3.0 ได้แล้ว :)

0 comments | Filed Under: Ruby | Tags: string

Ruby :: Converting between Characters and Values

Posted by PunNeng, Thu Mar 29 02:51:00 UTC 2007

ถ้าอยากจะดู ASCII code ของอักขระแต่ละตัว เราสามารถทำได้โดยใช้ ? วางไว้ข้างหน้าอักขระนั้นๆ เช่น

?a                       # => 97 
?!                       # => 33 
?\n                       # => 10

ถ้าจะดูในรูปของ Array ก็จะได้ดังนี้

'a'[0]                   # => 97 
'bad sound'[1]           # => 97

แต่ในทางกลับกัน เราสามารถเอา ASCII code แปลงกลับเป็นอักขระได้โดยการเรียก #chr method

97.chr                   # => "a" 
33.chr                   # => "!" 
10.chr                   # => "\n" 
0.chr                    # => "\000" 
256.chr                  # RangeError: 256 out of char range

ถ้าสมมติว่าเราไม่มองมันเป็น array ตัว string มันก็จะเหมือนกับการเอา Fixnum ตัวนึง มาเรียงต่อๆ กัน แต่ต้องเป็น ASCII code แล้วใช้ String#each_byte loop มันต่อกันจะออกมาเป็นตัวอักษรเอง

แก้ไขล่าสุด วันที่ 6 สิงหาคม 2550 เวลา 22.29 น.

0 comments | Filed Under: Ruby | Tags: string

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

0 comments | Filed Under: Ruby | Tags: string

Ruby :: Reversing a string by words or characters

Posted by PunNeng, Thu Feb 08 00:28:00 UTC 2007

คราวนี้ เราต้องการจะทำการเรียกย้อนกลับประโยค ทั้งในรูปแบบของคำและและตัวอักษร อันนี้ไม่ยาก เข้าใจง่าย โดยเราจะใช้ reverse method ในการ return ค่าที่เรียงเสร็จแล้ว แต่ string น้ันๆ ยังไม่เปลี่ียน แต่ถ้าต้องการให้ string นั้นๆ เปลี่ยนไปเลย ให้ใช้ reverse! method แทน

s = ".sdrawkcab si gnirts sihT"
s.reverse        # => "This string is backwards."
s                # => ".sdrawkcab si gnirts sihT"

s.reverse!       # => "This string is backwards."
s                # => "This string is backwards."

แต่ถ้าเราต้องการทำในรูปของคำล่ะ มาดูแบบนี้

s = "order. wrong the in are words These"
s.split(/(\s+)/).reverse!.join('')    # => "These words are in the wrong order."
s.split(/\b/).reverse!.join('')       # => "These words are in the wrong. order"

มาดูกัน String#split method จะรับ regular expression เข้ามาเพื่อทำการแยก string ออก แล้วค่อย return ออกมาเป็น array ของชุดที่ถูกแยกออกนั้น

ในส่วนของ regular expression ที่รับมาน้ัน คือ /(\s+)/ คือ เอาตัวที่เป็น white space ที่ติดกันมากกว่า 1 ตัวขึ้นไป ซึ่งหมายความว่าจะใช้ white space เป็นตัวแยก string ออกมา ซึ่ง white space นี จะต้องมีตั้งแต่ 1 ตัว หรือมากกว่าน้ันที่ติดๆ กัน ลองเข้า irb ดู แล้วพิมพ์ว่า

s = "order. wrong the in are words These"
s.split(/(s+)/)
# => ["order.", " ", "wrong", " ", "the", " ", "in", " ", "are", " ", "word", " ", "These"]

แต่ \b น้ัน หมายความว่า เอาที่เป็นขอบของคำ เพราะฉะน้ัน มันจะรวมพวกเครื่องหมายหรือสัญลักษณ์ต่างๆ มาด้วย ซึ่งหมายความว่า ใช้ขอบของคำ ซึ่งในที่นี้ ขอบของคำก็จะเป็นสิ่งที่อยู่ระหว่างคำ ซึ่งก็อาจจะรวมพวกเครื่องหมายต่างๆ เข้ามาด้วย ถ้ามันมี เป็นตัวแยก string ออกมา ลองพิมพ์ใน irb ต่อ จะได้

s.split(/\b/)
# => ["order", ". ", "wrong", " ", "the", " ", "in", " ", "are", " ", "word", " ", "These"]

พอได้แล้ว ก็เอามาทำการเรียงประโยคมาใหม่ แล้ว join กัน เอา array ที่ได้มา มา join กันให้เป็นประโยค แล้วทำไมต้อง join ด้วย '' ด้วย ก็เพราะว่าเรามี white space ในชุด array ของเราแล้ว

มีข้อสังเกตอยู่นิดนึง สำหรับ String#split ในกรณีนี้ ถ้าใน regular expression ของเราที่เราจะไว้เป็นตัวแยก อยู่ในวงเล็บ "()" เช่น /(\s*)/ อันนี้ มันจะรวมตัวแยกไปอยู่ใน array ด้วย ลองดูข้อแตกต่างของสองตัวอย่างนี้ครับ

"Three little  words".split(/\s+/)
# => ["Three", "little", "words"]
"Three little  words".split(/(\s+)/)
# => ["Three", " ", "little", "  ", "words"]

ปล. ผมเกลียด regular expression ไว้จะมาเจอกับมันเต็มๆ อีกที หึหึ

แก้ไขล่าสุด วันที่ 31 กรกฏาคม 2550 เวลา 1.12 น.

0 comments | Filed Under: Ruby | Tags: string

Ruby :: Substituting variables into an existing string

Posted by PunNeng, Thu Jan 25 11:23:00 UTC 2007

ถ้าเราต้องการจะสร้าง string โดยที่ต้องการใส่ตัวแปรลงไปด้วยในก้อน string น้ันๆ อาจจะทำได้จากการใช้ #{} แทรกเข้าไปใน string เช่น

"#{my_variable}"

ถ้าต้องการเปลี่ยน ก็เพียงแค่เปลี่ยนค่า my_variable เช่น my_variable = "new string or expression" หรือว่า my_variable = any_variable จริงๆ แล้ว เราไม่ต้องมาทำการเซ็ตค่าให้ my_variable ตลอด ใน ruby มี 2 วิธี ที่คอยจัดการให้ของพวกนี้ง่ายขึ้่น คือ แบบ printf และ ERB template มาดูทางด้าน printf ตามแนวของ C หรือ python ก่อน

template = 'Oceania has always been at war with %s.%'
template % 'Eurasia'  # => "Oceania has always been at war with Eurasia."
template % 'Eastasia' # => "Oceania has always been at war with Eastasia."

'To 2 decimal places: %.2f' % Math::PI     # => "To 2 decimal places: 3.14"
'Zero-padded: %.5d' % Math::PI             # => "Zero-padded: 00003"

มาดูทางด้าน ERB บ้าง
มันก็มีลักษณะคล้ายๆ กับ php หรือ jsp นั่นแหละ เพียงแต่เปลี่ยนเป็น code ของ ruby เท่านั้น เช่น php จะใช้ <?php //php code ?> ฝั่ง ruby จะเป็น <% #ruby code %> ซึ่งมันอาจจะเป็น expression หรือ ตัวแปร ก็ได้ มาดูตัวอย่าง

require 'erb'

template = ERB.new %q{Chunky <= food %>!}
food = "bacon"
template.result(binding)                   # => "Chunky bacon!"
food = "peanut butter"
template.result(binding)                   # = > "Chunky peanut butter!"

ตัว %q{} ทำหน้าที่สร้าง single quote string เราสามารถใช้ %Q{} ในการสร้าง double quote string ด้วยเช่นกัน ถ้าเราไม่ได้รันอยู่บน irb เราสามารถใช้ template.result ได้เลย ข้ามการเรียก Kernel#binding ไปได้เลย
ตัว .rhtml ของ rails ก็ใช้ตัว ERB นี่แหละครับ ในการแสดงผล นอกจากตัวอย่างข้างต้น ใน ERB ยังสามารถใช้ loops หรือ conditions ได้อีกด้วย ลักษณะเหมือน serverside script ทั่วๆ ไป โดยมีข้อสังเกตนิดนึงคือ <% %> ไว้สำหรับ expression หรือ loop หรือ conditions ต่างๆ นานา มันจะไม่แสดง(print) ออกมา แต่ถ้าเราใช้ <%= %> มันจะเป็นการแสดงสิ่งที่อยู่ข้างในออกมา ยังมีทางเลือกอื่นอืีกครับ จากอันนี้ เรารู้ว่าถ้าใช้ single quote เจ้าตัว #{} มันจะใช้ไ่ม่ได้ เราสามารถใช้ eval ช่วยได้

class String
  def substitute(binding=TOPLEVEL_BINDING)
    eval(%{"#{self}"}, binding)
    # %{} <=> here string
  end
end

template = %q{Chunky %{food}!}                # => "Chunky \#{food}!"
# %q{} <=> single quote
food = 'bacon'
template.substitue(binding)                   # => "Chunky bacon!"
food = 'peanut butter'
template.substitue(binding)                   # => "Chunky peanut butter!"

การใช้ eval นี้ต้องระวังหน่อย เราอาจจะเจอการฉีด(injection) อะไรบางอย่างเข้ามาใจ code ของเราโดยตรง อาจจะเป็นช่องว่างในการเปิดให้ชาวบ้านมาโจมตีได้ เช่น อาจจะโดนฉีด command มา มันก็จะทำการรัน command นั้นๆ เช่น

eval("system('ls')",binding)

ถ้าอยู่ในรูปแบบนี้ละก็ เสร็จแน่ ตัว system มันเป็น method นึงของคลาส kernel มันจะทำการรันคำสั่ง ls ตามที่เราใส่เข้าไป งืมๆๆ แต่ตัวอย่างที่่ให้มา ถูกจัดการเรียบร้อยแล้ว ด้วยการห่อด้วย single quote อีกทีนึง ตอนที่เราสั่ง food = '#{system("ls")}' มันจะมองให้ถูกเป็นแค่ string เท่านั้น ถ้าเปลี่ยนเป็น double quote ละก็ เป็นเรื่อง ลองดูคำสั่งนี้

template = %q{#{food}!}
food = '#{system("ls")}'
puts template.substitute(binding)
# Chunky #{system("ls")}!

มันจะถูกมองเป็น string เท่านั้น ลองเปลี่ยนเป็น food = "#{system("ls")}" นะครับ

แก้ไขล่าสุด วันที่ 24 กรกฏาคม 2550 เวลา 22.49 น.

0 comments | Filed Under: Ruby | Tags: string

codegent: we're hiring