โมดูล `io` ในภาษาไพธอน

โมดูล `io` ในภาษาไพธอน

บทความนี้อธิบายเกี่ยวกับโมดูล io ในภาษาไพธอน

เราจะอธิบายโมดูล io ในภาษาไพธอนพร้อมตัวอย่างการใช้งานจริง

YouTube Video

โมดูล io ในภาษาไพธอน

การประมวลผลข้อมูลขาเข้า/ขาออก (I/O) เป็นรากฐานของการทำงานกับข้อมูลทุกรูปแบบ เช่น ไฟล์ เครือข่าย และ I/O มาตรฐาน โมดูล io ของไพธอนมีคลาสนามธรรม (abstract classes) ที่รวมการประมวลผลข้อมูลขาเข้า/ขาออกเหล่านี้ไว้ด้วยกัน แนวคิดสำคัญในการเข้าใจโมดูลนี้ คือแนวคิดเรื่อง "สตรีม (stream)"

สตรีมคืออะไร?

สตรีมคือการไหลของข้อมูลที่เป็นนามธรรมสำหรับการอ่านและเขียนข้อมูลแบบต่อเนื่องตามลำดับ

ไม่ว่าจะอ่านข้อมูลไฟล์ทีละไบต์หรือส่งและรับข้อมูลผ่านเครือข่าย สิ่งเหล่านี้สามารถจัดการในรูปแบบของ data stream ได้ทั้งหมด

ด้วยการทำให้อุปกรณ์เหล่านี้เป็นนามธรรมขึ้น เราจึงสามารถจัดการกับไฟล์, หน่วยความจำ และเครือข่าย (ซึ่งเป็นแหล่ง I/O ต่างชนิดกัน) ด้วยการดำเนินการพื้นฐานเช่น การอ่านและเขียนในรูปแบบเดียวกัน

โมดูล io ของไพธอนมีส่วนติดต่อเดียวสำหรับสตรีม ทำให้สามารถจัดการข้อมูลทั้งแบบข้อความและข้อมูลเลขฐานสองได้อย่างมีประสิทธิภาพ

โครงสร้างพื้นฐานของโมดูล io

โมดูล io มีโครงสร้างเป็นสามชั้นตามธรรมชาติของสตรีม

  1. เลเยอร์ Raw (RawIOBase)

    RawIOBase จัดการ I/O ระดับไบต์ที่ต่ำที่สุด เช่นตัวรับไฟล์ของระบบปฏิบัติการและอุปกรณ์

  2. เลเยอร์ Buffered (BufferedIOBase)

    BufferedIOBase มีแคช (buffer) สำหรับช่วยเพิ่มประสิทธิภาพในการทำงาน I/O BufferedReader และ BufferedWriter เป็นตัวอย่างที่พบได้บ่อย

  3. เลเยอร์ Text (TextIOBase)

    TextIOBase แปลงลำดับไบต์เป็นสตริงและจัดการการเข้ารหัส โดยปกติเมื่อเปิดไฟล์ด้วยฟังก์ชัน open() จะใช้ TextIOWrapper ที่อยู่ในเลเยอร์นี้

ด้วยโครงสร้างนี้ โมดูล io สามารถแยกการจัดการข้อมูลแบบข้อความและข้อมูลเลขฐานสอง (binary) ออกจากกัน พร้อมทั้งรองรับการนำไปใช้ร่วมกันได้อย่างยืดหยุ่น

โครงสร้างพื้นฐานของโมดูล io

RawIOBase จัดการตัวรับไฟล์ของระบบปฏิบัติการที่เลเยอร์ล่างสุด, มี BufferedIOBase ซึ่งเป็นตัวแคชในชั้นถัดขึ้นไป และ TextIOBase ที่เลเยอร์บนสุดสำหรับแปลงเป็นสตริง

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • โค้ดนี้ใช้ตรวจสอบกลุ่มคลาสนามธรรมที่สืบทอดมาจาก IOBase คุณจะเห็น TextIOBase, BufferedIOBase และ RawIOBase ซึ่งแสดงถึงโครงสร้างเป็นลำดับชั้น

io.IOBase: คลาสพื้นฐานของทุกคลาส

IOBase เป็นคลาสนามธรรมพื้นฐานของอ็อบเจ็กต์ I/O ทั้งหมด โดยกำหนดเมธอดหลักๆ เช่น close(), flush(), และ seekable() ปกติเราจะไม่ใช้งานโดยตรง แต่จะเข้าถึงผ่านคลาสที่สืบทอด

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • ตัวอย่างนี้แสดงให้เห็นว่าเมธอดหลักๆ ใน IOBase สามารถใช้ได้ในคลาสระดับสูงที่สืบทอดมา seekable() และ readable() มีประโยชน์สำหรับการตรวจสอบคุณสมบัติของสตรีม

io.RawIOBase: เลเยอร์ระดับต่ำสุด

RawIOBase เป็นเลเยอร์ที่ใกล้กับตัวรับไฟล์ของระบบปฏิบัติการมากที่สุด และไม่ได้ทำการบัฟเฟอร์ ตัวอย่างการใช้งานที่พบได้ทั่วไปคือ FileIO ซึ่งเป็นคลาสสำหรับอ่านและเขียนทีละไบต์

1import io, os
2
3# Create a low-level FileIO object (no buffering)
4fd = os.open('raw_demo.bin', os.O_RDWR | os.O_CREAT)
5raw = io.FileIO(fd, mode='w+')
6raw.write(b'abc123')
7raw.seek(0)
8print(raw.read(6))  # b'abc123'
9raw.close()
  • FileIO เป็นคลาสที่ใช้งานได้จริงจาก RawIOBase โดยทุกคำสั่งอ่านและเขียนจะทำงานกับข้อมูลในรูปแบบ bytes สามารถเพิ่มประสิทธิภาพได้โดยนำไปรวมกับเลเยอร์ BufferedIOBase ที่สูงขึ้น

io.BufferedIOBase: เลเยอร์กลาง (มีบัฟเฟอร์)

BufferedIOBase เป็นเลเยอร์กลางที่ทำหน้าที่บัฟเฟอร์ข้อมูลเพื่อเพิ่มประสิทธิภาพในการเข้าถึงดิสก์ คลาสที่นำมาประยุกต์ใช้หลักๆ ได้แก่ BufferedReader, BufferedWriter, BufferedRandom และ BufferedRWPair

1import io
2
3# Create a buffered binary stream on top of a BytesIO (simulate file)
4base = io.BytesIO()
5buffered = io.BufferedWriter(base)
6buffered.write(b'Python IO buffering')
7buffered.flush()
8base.seek(0)
9print(base.read())  # b'Python IO buffering'
  • ในตัวอย่างนี้ ข้อมูลที่เขียนผ่าน BufferedWriter จะเก็บไว้ในบัฟเฟอร์หน่วยความจำชั่วคราว และจะถูกส่งลงไปที่เลเยอร์ล่างก็ต่อเมื่อมีการเรียก flush()

ตัวอย่างของ BufferedReader

BufferedReader เป็นสตรีมบัฟเฟอร์สำหรับอ่านอย่างเดียวที่รองรับการอ่านข้อมูลอย่างมีประสิทธิภาพด้วย peek() และ read()

1import io
2
3stream = io.BytesIO(b"1234567890")
4reader = io.BufferedReader(stream)
5print(reader.peek(5))   # b'12345' (non-destructive)
6print(reader.read(4))   # b'1234'
7print(reader.read(3))   # b'567'
  • peek() เพียงแค่ดูข้อมูลโดยไม่ขยับตัวชี้ตำแหน่งข้อมูล สามารถควบคุมการบัฟเฟอร์ข้อมูลได้อย่างยืดหยุ่นโดยการใช้งานร่วมกับ read()

io.TextIOBase: เลเยอร์สำหรับข้อความเท่านั้น

TextIOBase เป็นชั้นนามธรรมสำหรับจัดการสตริง โดยภายในจะทำการถอดรหัสและเข้ารหัสข้อมูล ตัวอย่างคลาสที่พบได้บ่อยคือ TextIOWrapper

 1import io
 2
 3# Wrap a binary stream to handle text encoding
 4binary = io.BytesIO()
 5text_stream = io.TextIOWrapper(binary, encoding='utf-8')
 6text_stream.write("\u3053\u3093\u306B\u3061\u306F")
 7text_stream.flush()
 8
 9# Reset stream position
10binary.seek(0)
11
12# Read bytes once
13data = binary.read()
14
15# Show both raw bytes and decoded text
16print("Raw bytes:", data)
17print("Decoded text:", data.decode('utf-8'))
  • ในตัวอย่างนี้ TextIOWrapper จะเข้ารหัสสตริงเป็น UTF-8 แล้วเขียนลงในสตรีมไบนารี่ด้านล่าง

ตัวอย่างการอ่านข้อมูลด้วย TextIOWrapper

การแปลงรหัสจะทำงานอัตโนมัติเมื่ออ่านข้อมูล

1import io
2
3binary_data = io.BytesIO("Python I/O".encode('utf-8'))
4text_reader = io.TextIOWrapper(binary_data, encoding='utf-8')
5print(text_reader.read())  # 'Python I/O'
  • TextIOWrapper เป็นคลาสหลักสำหรับ I/O แบบข้อความ และถือเป็นพื้นฐานของการดำเนินการกับไฟล์ในระดับสูงเกือบทั้งหมด

io.StringIO: สตรีมข้อความในหน่วยความจำ

StringIO เป็นคลาสที่ช่วยให้คุณสามารถจัดการกับสตริงในหน่วยความจำราวกับว่าเป็นไฟล์ เหมาะสำหรับใช้ทดสอบ I/O หรือสร้างข้อมูลชั่วคราว

1import io
2
3text_buf = io.StringIO()
4text_buf.write("In-memory text stream")
5text_buf.seek(0)
6print(text_buf.read())  # 'In-memory text stream'
  • StringIO ช่วยให้ดำเนินการเหมือนไฟล์ได้โดยไม่ต้องใช้ดิสก์ และนิยมนำไปใช้ในการทดสอบหน่วย (unit testing)

io.BytesIO: สตรีมไบนารี่ในหน่วยความจำ

BytesIO เป็นคลาสไฟล์ในหน่วยความจำสำหรับจัดการลำดับไบต์ (bytes) เหมาะสำหรับกรณีที่ต้องประมวลผลข้อมูลเลขฐานสองหรือบีบอัดข้อมูลโดยไม่ต้องเขียนลงไฟล์

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO มีส่วนติดต่อเช่นเดียวกับ BufferedIOBase และใช้แทนไฟล์ api ได้หลายกรณี

การสร้างสตรีมแบบกำหนดเอง (Custom Stream)

คลาสต่างๆ ในโมดูล io สามารถขยายเพิ่มเติมได้ ทำให้คุณสร้างคลาสสตรีมของคุณเองได้ ด้านล่างนี้คือตัวอย่างการทำซับคลาสของ TextIOBase ที่แปลงข้อความทั้งหมดให้เป็นตัวพิมพ์ใหญ่ขณะเขียน

 1import io
 2
 3class UpperTextIO(io.TextIOBase):
 4    def __init__(self):
 5        self.buffer = ""
 6    def write(self, s):
 7        self.buffer += s.upper()
 8        return len(s)
 9
10u = UpperTextIO()
11u.write("hello io")
12print(u.buffer)  # "HELLO IO"
  • ตราบใดที่คุณปฏิบัติตามข้อกำหนดของ TextIOBase คุณก็สามารถกำหนดพฤติกรรมเฉพาะได้ตามต้องการ สามารถขยายการทำงานของสตรีมให้เหมาะกับการใช้งานเฉพาะ เช่น ไฟล์หรือเครือข่ายได้อย่างง่ายดาย

สรุป

โมดูล io จัดระเบียบการประมวลผล I/O เป็นลำดับชั้นของคลาสนามธรรมและคลาสที่นำไปใช้ได้จริง

  • RawIOBase คือคลาสสำหรับ I/O ระดับไบต์ในระบบปฏิบัติการ
  • BufferedIOBase คือคลาสที่ให้ชั้นแคชอย่างมีประสิทธิภาพ
  • TextIOBase คือคลาสที่จัดการงานอ่าน/เขียนข้อความ
  • StringIO และ BytesIO คือคลาสที่ให้สตรีมข้อมูลในหน่วยความจำ

การเข้าใจคลาสเหล่านี้ทำให้คุณสามารถเข้าใจการทำงานของระบบ I/O ในภาษาไพธอนได้อย่างถูกต้อง และนำไปประยุกต์ใช้ในงานกับไฟล์, ติดต่อเครือข่าย และออกแบบสตรีมสำหรับทดสอบได้

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video