หัดเขียนโปรแกรมแบบบ้านๆ ตอนที่ 8 การจัดการไฟล์

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


การจัดการไฟล์

กลับมาแล้วครับหายไปนานเลยตอนนี้พอว่างแล้วกลับมาทำบทความต่อแล้วครับ

แบบใช้ฟังก์ชันในมอดูล IO

Lua มีการจัดการเขียนอ่านไฟล์สองแบบอย่างแรกคือการใช้ฟังก์ชันในมอดูล io ตามที่เคยกล่าวถึงในตอนที่ 5 แล้วซึ่งมีรายละเอียดดังนี้

io.open()

เป็นฟังก์ชันเปิดไฟล์เพื่ออ่านเขียนข้อมูลโดยมีโหมดต่างๆ ดังนี้
  r           เปิดไฟล์ที่มีอยู่เพื่ออ่านอย่างเดียว
  w           เปิดไฟล์เพื่อเขียนทับข้อมูลเดิมหรือสร้างไฟล์ใหม่ถ้าไม่มีไฟล์นั้นอยู่
  a           เปิดไฟล์เพื่อเขียนข้อมูลต่อท้ายข้อมูลเดิมหรือสร้างไฟล์ใหม่ถ้าไม่มีไฟล์นั้นอยู่
  r+          เปิดไฟล์ที่มีอยู่เพื่ออ่านและเขียน
  w+          เปิดไฟล์เพื่ออ่านและเขียนโดยจะลบข้อมูลเก่าหรือสร้างไฟล์ใหม่
  a+          เปิดไฟล์เพื่ออ่านและเขียนต่อท้ายข้อมูลเดิมหรือสร้างไฟล์ใหม่
  rb          เหมือน r แต่เป็น binary mode ไม่ใช่ text mode
  wb          เหมือน w แต่เป็น binary mode ไม่ใช่ text mode
  ab          เหมือน a แต่เป็น binary mode ไม่ใช่ text mode
  r+b         เหมือน r+ แต่เป็น binary mode ไม่ใช่ text mode
  w+b         เหมือน w+ แต่เป็น binary mode ไม่ใช่ text mode
  a+b         เหมือน a+ แต่เป็น binary mode ไม่ใช่ text mode
  
  ตัวอย่าง
  
  local f = io.open("test.txt","r")     -- เปิดไฟล์ test.txt เก็บไว้ที่ตัวแปร f เพื่ออ่านข้อมูล
  io.input(f)                           -- กำหนดให้ input ชี้ไปที่ f ( test.txt )
  print(io.read())                      -- อ่านข้อมูลบรรทัดแรก
  print(io.read())                      -- อ่านข้อมูลบรรทัดถัดไป
  io.close(f)                           -- ปิดไฟล์
  
  f = io.open("test.txt","a")           -- เปิดไฟล์ test.txt เก็บไว้ที่ตัวแปร f เพื่อเขียนข้อมูลต่อท้าย
  io.output(f)                          -- กำหนดให้ output ชี้ไปที่ f
  io.write("\nNew line")                -- เขียนข้อความ "New line" ต่อท้ายบรรทัดใหม่ใน f
  io.write("Same line")                 -- เขียนข้อความ "Same line" ต่อท้ายบรรทัดเดิมใน f
  io.close(f)                           -- บันทึกข้อมูลใน f แล้วปิดไฟล์ ( ข้อความบรรทัดสุดท้ายไฟล์ test.txt --> "New lineSame line" )
  
  io.input(io.stdin)                   -- ให้ input กลับมารับค่าจากคีย์บอร์ด
  io.output(io.stdout)                 -- ให้ output กลับไปชี้ที่จอ
  

io.read()

มีรูปแบบการรับข้อมูลดังนี้
  n           รับข้อมูลตัวเลขที่ตำแหน่งปัจจุบันแล้วแปลงเป็นข้อมูลชนิด number ถ้าไม่ใช่ตัวเลขจะได้ค่าเป็น nil 
              ( รุ่น 5.1, 5.2 ใช้ *n หรือ *number )
  a           รับข้อมูลทั้งไฟล์ถ้าตำแหน่งที่รับข้อมูลเป็นตัวจบไฟล์ ( EOF ) จะคืนค่าว่าง ( "" ) 
              ( รุ่น 5.1, 5.2 ใช้ *a หรือ *all )
  l           รับข้อมูลที่ตำแหน่งปัจจุบันจนจบบรรทัดไม่รวมตัวจบบรรทัด ( EOL ) แล้วย้ายไปตำแหน่งบรรทัดถัดไป 
              ถ้าตำแหน่งที่รับค่าเป็นตัวจบไฟล์ ( EOF ) จะคืนค่า nil ( รุ่น 5.1, 5.2 ใช้ *l หรือ *line )
  L           เหมือน l แต่จะรวม EOL ด้วย ( รุ่น 5.2 ใช้ *L )
  ตัวเลข       รับค่าข้อมูลตาจำนวน bytes ที่กำหนด
  ถ้าไม่กำหนดรูปแบบจะใช้ l เป็นค่าตั้งต้น
  
  ตัวอย่าง
  
  --[[ ข้อมูลไฟล์ test2.txt
  10 10
  asdff
  ]]
  -- Ex.1
  a = io.read()
  b = io.read()
  c = io.read()
  print(type(a),a)         -- "string"     "10 10"
  print(b)                 -- "asdff"
  print(c)                 -- nil
  -- Ex.2
  a = io.read("n")
  b = io.read()
  c = io.read("n")
  d = io.read()
  print(type(a),a)         -- "number"     10
  print(type(b))           -- " 10"
  print(c)                 -- nil
  print(d)                 -- "asdff"
  -- Ex.3
  a = io.read("a")
  print(a)                 -- "10 10\nasdff\n"
  -- Ex.4
  a,b = io.read("n","n")
  c = io.read(3)
  print(a)                 -- 10
  print(b)                 -- 10
  print(c)                 -- "asd"
  

io.lines()

มีรูปแบบการรับข้อมูลเหมือน io.read() แต่คืนค่าเป็น iterator 
  ตัวอย่าง
  
  --[[ ข้อมูลไฟล์ test2.txt
  10 10
  asdff
  ]]
  -- Ex.1
  a = {}
  for line in io.lines("test2.txt") do
    table.insert(a,line)
  end
  print(#a, a[1])          -- 2            "10 10"
  print(a[2])              -- "asdff"
  print(a[3])              -- nil
  -- Ex.2
  a = {}
  for line in io.lines("test2.txt","n") do
    table.insert(a,line)
  end
  print(#a, a[1])          -- 2            10
  print(a[2])              -- 10
  print(a[3])              -- nil
  -- Ex.3
  a = {}
  for line in io.lines("test2.txt",3) do
    table.insert(a,line)
  end
  print(#a, a[1])          -- 4            "10 "
  print(a[2])              -- "10\n"
  print(a[3])              -- "asd"
  print(a[4])              -- "ff\n"
  print(a[5])              -- nil
  

แบบใช้เมธอดในอ๊อบเจกต์ไฟล์

โดยมี method ให้ใช้ดังนี้
  • read([...])  อ่านข้อมูลจากอ๊อบเจกต์ไฟล์รูปแบบการใช้งานเหมือน io.read()
  • seek([option][,pos])  คืนค่าเป็นตำแหน่ง byte ปัจจุบันที่จะอ่านข้อมูลโดยสามารถกำหนดตำแหน่งเริ่มต้น ( pos ) ได้โดยใช้คู่กับ option ถ้าไม่กำหนด option จะใช้ "cur" ( current ) เป็น option ( ค่า option ได้แก่ "cur" ให้ตำแหน่งเริ่มต้นเป็นตำแหน่งปัจจุบัน, "set" กำหนดตำแหน่งเริ่มต้นเป็นตำแหน่งต้นไฟล์ และ "end" กำหนดตำแหน่งเริ่มต้นเป็นตำแหน่งจบไฟล์ ) pos เป็นการเพิ่ม/ลดตำแหน่งจากที่กำหนดใน option ถ้าไม่กำหนดจะใช้ค่าเป็น 0
    ตัวอย่าง "hello" ตำแหน่งเริ่มต้นคือ 0 ได้แก่ "h" ตัวสุดท้าย "o" คือตำแหน่งที่ 4 และตำแหน่งจบไฟล์ ( "end" ) คือตำแหน่งที่ 5
  • write(...)  เขียนข้อมูลลงอ๊อบเจกต์ไฟล์รูปแบบการใช้งานเหมือน io.write()
  • lines([format])  คืนข้อมูลเป็น iterator เหมือน io.lines()
  • flush()  บันทึกข้อมูลใน buffer ลงไฟล์เหมือน io.flush()
  • setvbuf(option[,size])  กำหนดการเก็บ buffer โดยที่ option "no" จะไม่เก็บ buffer, "full" จะเก็บจนกว่า buffer จะเต็มหรือสั่ง flush สามารถกำหนด size ได้, "line"  จะเก็บจนเจอตัวขึ้นบรรทัดใหม่ ( \n )
  ตัวอย่าง
  
  local f = io.open("test2.txt","a+")
  print(type(f))           -- "userdata"
  print(f)                 -- file (0xe46100)
  local a,b
  print(f:seek())          -- 0 ( ตำแหน่งเริ่มต้นข้อมูลคือ 0 )
  a = f:read()
  print(a,f:seek())        -- "10 10"      6 ( ตำแหน่งเริ่มต้นบรรทัดถัดไป )
  print(f:seek("cur",2))   -- 8 ( เลื่อนจากตำแหน่งปัจจุบันไป 2 bytes )
  b = f:read()
  print(b,f:seek())        -- "dff"        12 ( ตำแหน่งท้ายไฟล์ "10 10\nasdff\n" 12 bytes จาก 0-11 )
  print(f:seek("cur",-6))  -- 6 ( เลื่อนจากตำแหน่งปัจจุบันย้อนกลับไป 6 bytes )
  b = f:read("L")          -- อ่านแบบรวมตัว EOL ด้วย
  print(b)                 -- "asdff\n"
  
  f:seek("set")            -- ย้อนกลับไปตำแหน่ง 0
  local c = {}
  for l in f:lines(3) do
    table.insert(c,l)
  end
  print(#c, c[1])          -- 4            "10 "
  print(c[2])              -- "10\n"
  print(c[3])              -- "asd"
  print(c[4])              -- "ff\n"
  print(c[5])              -- nil
  
  f:write("qqq\n")         -- เขียนข้อมูลลง buffer ยังไม่บันทึกลงไฟล์ต้องสั่ง flush หรือ close ก่อนหรือไม่ก็ใช้ setvbuf("no") ก็จะไม่เก็บ buffer แต่ส่งไปที่ output ( ไฟล์ ) เลย
  f:flush()                -- บันทึกลงไฟล์
  a = f:read()
  b = f:read("a")
  print(a,b)               -- nil          ""
  f:seek("set")
  a = f:read("a")          -- "10 10\nasdff\nqqq\n"
  f:write("zzz\n")
  f:close()                -- บีนทึกลงไฟล์พร้อมปิดไฟล์
  print(f)                 -- file (closed)
สำหรับใน Lua 5.4 จะมี attribute <close> สำหรับตัวแปรที่จะไปเรียก metamethod __close เมื่อออกจากบล๊อกที่ตัวแปรถูกสร้าง ในกรณีที่ตัวแปรเป็น file เมื่อออกจากบล๊อกจะปิดไฟล์เองโดยไม่ต้องเรียกใช้ io.close()
  ตัวอย่าง
  
  do
    local f <close> = io.open("test2.txt","a+")
    local c = {}
    for l in f:lines() do
      table.insert(c,l)
    end
    f:write("yyy\n")
  end                      -- ปิดไฟล์อัตโนมัติเมื่อจบบล๊อก
  
  print(#c, c[1])          -- 4            "10 10"
  print(c[2])              -- "asdff"
  print(c[3])              -- "qqq"
  print(c[4])              -- "zzz"
  print(c[5])              -- nil
  
  local f2 = io.open("test2.txt")
  print(f2:read("a"))      -- "10 10\nasdff\nqqq\nzzz\nyyy\n"
  f2:close()
ตอนต่อไปมารู้จัก Coroutine กันครับ


ความคิดเห็น