หัดเขียนโปรแกรมแบบบ้านๆ ตอนที่ 7 String format และ Lua pattern

บทความก่อนหน้า

String format และ Lua pattern

string.format()

เป็นฟังก์ชันจัดรูปแบบข้อความคล้ายกับ sprintf ในภาษา C, C++, Perl, PHP, R, Ruby หรือ % operator ใน Python, Ruby โดยมีรูปแบบของการจัดรูปแบบข้อความดังนี้
%[flags][ความกว้าง][.จำนวนทศนิยม]รูปแบบ

flags ได้แก่
  +           แสดงเครื่องหมาย + หน้าตัวเลขบวก
  เว้นวรรค     แสดงช่องว่างหน้าตัวเลขบวก
  -           ใช้คู่กับ ความกว้าง แสดงตัวเลขชิดซ้าย
  0           ใช้คู่กับ ความกว้าง แสดง 0 แทนช่องว่าง
  #           ใช้คู่กับ รูปแบบ x หรือ X แสดง 0x หรือ 0X หน้าตัวเลขฐาน 16
              ใช้คู่กับ รูปแบบ o แสดง 0 หน้าตัวเลขฐาน 8
              ใช้คู่กับ รูปแบบ e หรือ E หรือ f บังคับแสดงจุดทศนิยม
              ใช้คู่กับ รูปแบบ g หรือ G บังคับแสดงจุดทศนิยมและไม่ตัด 0 ทิ้ง
              
ความกว้าง      ใช้กำหนดจำนวนตัวอักษรทั้งหมดที่จะแสดง
              ถ้าข้อมูลมีจำนวนตัวอักษรเกิน ความกว้าง จะแสดงตามจำนวนตัวอักษรของข้อมูล
              ถ้าข้อมูลมีจำนวนตัวอักษรน้อยกว่า ความกว้าง จะเติมช่องว่างด้านซ้ายข้อมูลเว้นแต่มี flags - จะเติมช่องว่างด้านขวาแทน
              
.จำนวนทศนิยม   ใช้ระบุจำนวนทศนิยมที่จะแสดงใน รูปแบบ f, e, E, g, G
              ถ้าใส่ . แต่ไม่มีตัวเลขจะเป็นการปัดเศษทศนิยม ( round ) เป็นจำนวนเต็ม
              
รูปแบบ ได้แก่
  s           แสดงข้อมูลเป็นแบบตัวอักษร
  q           แสดงข้อมูลตัวอักษรพร้อมใส่ \" \" ครอบถ้าในข้อความมี " จะแปลงเป็น \\\" ให้
  c           แปลงตัวเลขเป็นตัวอักษรรหัส ASCII ( 0-255 )
  d, i        แสดงข้อมูลตัวเลขจำนวนเต็มฐานสิบ ( 0x4d2 -> "1234", "123.0" -> "123" )
  u           แสดงข้อมูลตัวเลขจำนวนเต็มฐานสิบแบบไม่มีเครื่องหมาย ( -1 -> 18446744073709551615 )
  x           แสดงข้อมูลตัวเลขจำนวนเต็มฐานสิบหกแบบอักษรตัวเล็ก ( 1234 -> "4d2" )
  X           แสดงข้อมูลตัวเลขจำนวนเต็มฐานสิบหกแบบอักษรตัวใหญ่ ( 1234 -> "4D2" )
  o           แสดงข้อมูลตัวเลขจำนวนเต็มฐานแปด ( 1234 -> "2322" )
  f           แสดงข้อมูลตัวเลขทศนิยมฐานสิบ ( 2 -> "2.000000", 1.234e+02 -> "123.4000" )
  e           แสดงข้อมูลตัวเลขทศนิยมรูปแบบวิทยาศาสตร์ฐานสิบสัญลักษณ์เลขยกกำลังตัวเล็ก ( 123 -> "1.230000e+02" )
  E           แสดงข้อมูลตัวเลขทศนิยมรูปแบบวิทยาศาสตร์ฐานสิบสัญลักษณ์เลขยกกำลังตัวใหญ่ ( 123 -> "1.230000E+02" )
  g           แสดงข้อมูลตัวเลขทศนิยมแบบสั้น
              ทศนิยมรูปแบบวิทยาศาสตร์ฐานสิบสัญลักษณ์เลขยกกำลังตัวเล็กแบบสั้น
              หรือจำนวนเต็มฐานสิบ ( ตัดศูนย์ทิ้ง 12.300 -> "12.3" )
  G           แสดงข้อมูลตัวเลขทศนิยมแบบสั้น
              ทศนิยมรูปแบบวิทยาศาสตร์ฐานสิบสัญลักษณ์เลขยกกำลังตัวใหญ่แบบสั้น
              หรือจำนวนเต็มฐานสิบ ( ตัดศูนย์ทิ้ง 1.2300e+01 -> "1.23E+01" )
  a           แสดงข้อมูลตัวเลขทศนิยมฐานสิบหกแบบอักษรตัวเล็ก ( 1.234 -> "0x1.3be76c8b43958p+0" )
  A           แสดงข้อมูลตัวเลขทศนิยมฐานสิบหกแบบอักษรตัวใหญ่ ( 1.234 -> "0X1.3BE76C8B43958P+0" )
  %           แสดง %
  
  ตัวอย่าง
  
  print(string.format("Hello%8s. PI=%s (%-6.2f) (%03.f)","World",math.pi,math.pi,math.pi))
  ----> "Hello   World. PI=3.1415926535898 (3.14  ) (003)"
  
  print(("Hello%-8s. PI=%e (%10f) %% (% .3f)%s"):format("World",math.pi,math.pi,math.pi,"%"))
  ----> "HelloWorld   . PI=3.141593e+00 (  3.141593) % ( 3.142)%"
  

Lua pattern

คล้ายกับ Regular Expression ที่มีในหลายโปรแกรมแต่เป็นฉบับย่อสำหรับค้นหาข้อความที่มีรูปแบบตรงกับ pattern ที่กำหนดสำหรับใช้กับ string.find(), string.match(), string.gsub(), string.gmatch() มีรูปแบบดังนี้
  ^           ตัวอักขระที่ตามหลั้ง ^ จะต้องเป็นอักษรตัวแรกของข้อความ ( "^a" --> "abb" ตรง, "bab" ไม่ตรง )
  $           ตัวอักขระก่อน $ จะต้องเป็นตัวสุดท้ายของข้อความ ( "b$" --> "bab" ตรง, "bba" ไม่ตรง )
  .           ตัวอักขระอะไรก็ได้ ( "^a..b$" --> "ac b" ตรง, "axb" ไม่ตรง, "a2rdsb" ไม่ตรง )
  %a          ตัวอักษร a-z A-Z ( "3%ab" --> "3Xb" ตรง, "3.b" ไม่ตรง, "3*b" ไม่ตรง )
  %A          ตัวอักขระทุกตัวที่ไม่ใช่ a-z A-Z ( "3%Ab" --> "3#b" ตรง, "3 b" ตรง, "3eb" ไม่ตรง )
  %c          ตัวอักษรที่เป็นอักขระควบคุม ( control characters ) เช่น \n ( new line หรือ line feed ) \t ( tab ) \e ( escape )
  %C          ตัวอักษรทุกตัวที่ไม่ใช่อักขระควบคุม
  %d          ตัวเลข 0-9
  %D          ตัวอักขระทุกตัวที่ไม่ใช่ตัวเลข
  %g          ( Lua 5.2 ขึ้นไป ) ตัวอักขระที่แสดงผลได้ในรหัส ASCII ( printable characters เช่นตัวอักษร ตัวเลข เครื่องหมายวรรคตอน สัญลักษณ์ต่างๆ ) ทุกตัวยกเว้นตัวเว้นวรรค ( space )
  %G          ( Lua 5.2 ขึ้นไป ) ตัวอักขระที่ไม่ใช่ printable characters รวมถึง space
  %l          ตัวอักษรตัวเล็ก a-z
  %L          ตัวอักขระที่ไม่ใช่ a-z
  %p          เครื่องหมายวรรคตอนและสัญลักษณ์ ( '".,:;|/\~!?@#$%^&(){}[]+-*=<>_ )
  %P          ตัวอักขระที่ไม่ใช่เครื่องหมายใน %p
  %s          เว้นวรรค
  %S          อักขระทุกตัวที่ไม่ใช่เว้นวรรค
  %u          ตัวอักษรตัวใหญ่ A-Z
  %U          ตัวอักขระที่ไม่ใช่ A-Z
  %w          ตัวอักษรและตัวเลข ( alphanumeric characters a-z A-Z 0-9 )
  %W          ตัวอักขระที่ไม่ใช่ alphanumeric character
  %x          ตัวเลขฐานสิบหก ( hexadecimal digit a-f A-F 0-9 )
  %X          ตัวอักขระที่ไม่ใช่เลขฐานสิบหก
  %z          ตัวอักขระ "\000" 
  [set]       ตัวอักขระตัวใดตัวหนึ่งที่อยู่ใน set ( "[a-zA-Z]" เหมือนกับ "%a", "[0-7]" --> ตัวเลขฐานแปด, "[cD]" --> "c" หรือ "D" )
  [^set]      ตัวอักขระที่ไม่อยู่ใน set ( "[^A-Z0-9a-z]" เหมือนกับ "%W" )
  %f[set]     frontier pattern หาอักขระที่อยู่ระหว่างอักขระที่ไม่อยู่ใน set กับอักขระที่อยู่ใน set ( ("asdf1234"):gsub("%f[%d]","@") --> "asdf@1234" )
  %bxy        ตัวอักขระทุกตัวเริ่มตั้งแต่ x ตัวแรกจนถึง y ตัวสุดท้าย ( ("asdf{asde2354{} 45az  }"):match("%b{}") --> "{asde2354{} 45az  }" )
  (pattern)   capture pattern หาข้อความที่ตรงกับ pattern ใน () แล้วเก็บไว้อ้างถึงภายหลังได้ด้วย %n โดย n เป็นตัวเลขแสดงลำดับที่ของ ()
              ( ("abcde"):gsub("(%a)c(%a)"," %2%1 ") --> "a db e" ) แต่ถ้าข้างใน () ไม่ได้กำหนด pattern จะคืนค่าเป็นตำแหน่งอักขระแทน
              ( ("abcde"):gsub("()c()"," %2xx%1 ") --> "a 4xx3 e" ; a->1,b->2,c->3,d->4,e->5 )
  %อัขระพิเศษ   ใช้สำหรับต้องการหมายถึงตัวอักขระพิเศษที่ใช้ใน pattern เช่น ^ $ . % ( ) [ + - * ?
  
  ?           บอกจำนวนอักขระที่อยู่หน้า ? จำนวน 1 อักขระหรือไม่มีเลย
  +           บอกจำนวนอักขระที่อยู่หน้า + จำนวน 1 อักขระขึ้นไป
  *           บอกจำนวนอักขระที่อยู่หน้า * จำนวน 0 อักขระขึ้นไป
  -           บอกจำนวนอักขระที่อยู่หน้า - จำนวน 0 อักขระขึ้นไป ( จำนวนอักขระที่น้อยที่สุด )

ASCII กับ Unicode

เดิมอักขระในคอมพิวเตอร์มีแค่ตัวเลข ตัวอักษรภาษาอังกฤษ สัญลัษณ์เครื่องหมาย และอักขระควบคุม ซึ่งทั้งหมดใช้แค่ 1 byte ( 8 bits 00000000 ถึง 11111111 หรือ 0 ถึง 255 ) ก็เพียงพอโดยใช้รหัสแค่ 0-127 และส่วนที่เหลือสำรองไว้ใช้เป็นรหัสอักษรของภาษาท้องถิ่น โดยรหัสอักขระที่ใช้เรียกกันว่ารหัส ASCII ต่อมาเมื่อมีความต้องการให้รองรับกับภาษานานาชาติจึงได้มีการออกมาตรฐาน Unicode ขึ้นได้แก่ UTF-8, UTF-16, UTF-32 เป็นต้น
ในภาษา Lua นั้นไม่ได้รองรับ Unicode จนในรุ่น 5.3 ได้เพิ่มการรองรับ UTF-8 บางส่วนในมอดูล utf8 แต่ในส่วนของ pattern และในมอดูล string ยังถือว่าแต่ละอักขระใช้ 1 byte อยู่ ส่วน UTF-8 นั้นที่รหัส 0-191 ใช้ 1 byte จากนั้นจะเพิ่มทีละ byte ตามตารางในลิงค์ข้างบนโดย byte แรกเริ่มที่ 192 ใช้ 2 bytes, 224 ใช้ 3 bytes และ 240 ใช้ 4 bytes  ตามลำดับแต่ละ byte ที่เพิ่มขึ้นมาจะใช้รหัสตั้งแต่ 0-191

local text1 = "hello"
for char in text1:gmatch(".") do
  print(char)
end
--[[ ผลลัพธ์
H
e
l
l
o
]]

local text2 = "ครับ"
for char in text2:gmatch(".") do
  print(char)
end
--[[ ผลลัพธ์
\224    -- ค = \224\184\132 3 bytes
\184
\132
\224    -- ร = \224\184\163
\184
\163
\224    -- ั  = \224\184\177
\184
\177
\224    -- บ = \224\184\154
\184
\154
]]

print(string.gsub("abcกdeขf", "[c-e]", "Z"))
--[[ ผลลัพธ์
abZกZZขf     3
]]

print(string.gsub("abcกdeขf", "ก", "Z"))
--[[ ผลลัพธ์
abcZdeขf     1
]]

print(string.gsub("abcกdeขf","[ก]","Z"))  -- เท่ากับ [\129\184\224] --> \224 หรือ \129 หรือ \184
--[[ ผลลัพธ์
abcZZZdeZZ\130f     5                     -- ข เท่ากับ \224\184\130
]]

print(string.gsub("abcกdeขf","\224\184[\129\130]","Z"))
--[[ ผลลัพธ์
abcZdeZf     2
]]
อักษรภาษาไทยจะใช้ 3 bytes เริ่มที่ \224\184\128 เป็นค่าว่าง "ก" เริ่มที่ \129 ถึง "ฮ" ที่ \174 "ฯ" ที่ \175 สระเริ่มที่ \176 ถึง \186 และ \224\185\128 ถึง \133 และอักขระอื่นๆ ไปจนถึง \166 ดังนั้นสำหรับตัวอักษรภาษาไทยเราสามารถใช้รูปแบบได้ดังนี้ "\224[\184\185][\128-\191]" สำหรับตัวอักขระแต่ละตัวและ "\224[\184\185][\128-\224]+" สำหรับข้อความ หากอยากใช้ unicode ใน pattern เต็มรูปแบบต้องเขียนฟังก์ชันใช้เองหรือติดตั้งมอดูลเสริมแทนเช่น lua-utf8 ซึ่งเป็น C module หรือ utf8 ที่เป็น pure Lua หรือไปใช้ pattern matching อื่นๆ

บทความถัดไป

ความคิดเห็น