Ôn thi HSG Tin học THPT – Bài 8: Kiểu tệp văn bản (File I/O)

1. Mục tiêu

  • Hiểu rõ khái niệm và tầm quan trọng của việc nhập xuất dữ liệu qua tệp văn bản trong lập trình thi đấu.
  • Nắm vững cú pháp và sử dụng thành thạo hàm open() để thực hiện hai thao tác cơ bản: đọc dữ liệu từ tệp (read mode – ‘r’) và ghi dữ liệu ra tệp (write mode – ‘w’).
  • Áp dụng kỹ thuật xử lý tệp vào việc giải quyết các bài toán lập trình có yêu cầu nhập/xuất bằng tệp.

2. Giới thiệu và phạm vi ứng dụng

Trong lập trình nói chung và lập trình thi đấu nói riêng, việc xử lý dữ liệu không chỉ giới hạn ở luồng nhập chuẩn (bàn phím) và luồng xuất chuẩn (màn hình). Đối với các bài toán có khối lượng dữ liệu lớn hoặc yêu cầu lưu trữ kết quả lâu dài, việc sử dụng tệp (file) là một giải pháp tất yếu.

Tệp văn bản (text file) là dạng tệp lưu trữ dữ liệu dưới dạng các ký tự mà con người có thể đọc được. Trong các kỳ thi lập trình, nhiều bài toán yêu cầu thí sinh phải đọc dữ liệu đầu vào từ một tệp tin có tên cho trước (ví dụ: INPUT.TXT) và ghi kết quả ra một tệp tin khác (ví dụ: OUTPUT.TXT). Nắm vững kỹ năng xử lý tệp là yêu cầu cơ bản để có thể giải quyết trọn vẹn các dạng bài toán này.

3. Cú pháp và các ví dụ minh họa

3.1. Cấu trúc cú pháp chuẩn

Để làm việc với tệp trong Python, hàm open() được sử dụng. Cú pháp được khuyến khích và an toàn nhất là sử dụng câu lệnh with, giúp tự động đóng tệp sau khi hoàn tất khối lệnh, tránh các lỗi liên quan đến việc quên đóng tệp.

Cú pháp ghi tệp:

with open('ten_tep.txt', 'w', encoding='utf-8') as file_object:
    # Các lệnh ghi dữ liệu vào tệp
    file_object.write('noi dung can ghi')

Cú pháp đọc tệp:

with open('ten_tep.txt', 'r', encoding='utf-8') as file_object:
    # Các lệnh đọc dữ liệu từ tệp
    content = file_object.read()

Trong đó:

  • 'ten_tep.txt': Là tên của tệp cần thao tác.
  • 'w': Là chế độ mở tệp (mode). ‘w’ (write) có nghĩa là mở tệp để ghi. Nếu tệp chưa tồn tại, nó sẽ được tạo mới. Nếu tệp đã tồn tại, toàn bộ nội dung cũ sẽ bị xóa đi.
  • 'r': Là chế độ mở tệp. ‘r’ (read) có nghĩa là mở tệp để đọc. Nếu tệp không tồn tại, chương trình sẽ báo lỗi.
  • encoding='utf-8': Chỉ định bảng mã ký tự, rất quan trọng để đọc và ghi đúng các ký tự có dấu trong tiếng Việt.
  • file_object: Là biến đối tượng tệp, được sử dụng để gọi các phương thức như .write() hoặc .read().

3.2. Ví dụ 1: Ghi dữ liệu ra tệp văn bản

Ví dụ này minh họa cách tạo ra một tệp hocsinh.txt và ghi danh sách tên của ba học sinh vào tệp.

# Danh sách tên các học sinh
danh_sach_ten = ["Nguyễn Văn An", "Trần Thị Bình", "Lê Văn Cường"]

# Mở tệp 'hocsinh.txt' ở chế độ ghi ('w')
# encoding='utf-8' để đảm bảo ghi đúng tiếng Việt có dấu
with open('hocsinh.txt', 'w', encoding='utf-8') as f:
    # Duyệt qua từng tên trong danh sách
    for ten in danh_sach_ten:
        # Ghi tên vào tệp, kèm theo ký tự xuống dòng 'n'
        f.write(ten + 'n')

print("Đã ghi xong danh sách vào tệp hocsinh.txt")

Phân tích:

  • Chương trình sẽ tạo một tệp mới có tên hocsinh.txt trong cùng thư mục với tệp mã nguồn Python.
  • Phương thức f.write() được dùng để ghi một chuỗi ký tự vào tệp.
  • Ký tự 'n' được thêm vào cuối mỗi tên để mỗi tên nằm trên một dòng riêng biệt trong tệp văn bản.

3.3. Ví dụ 2: Đọc dữ liệu từ tệp văn bản

Ví dụ này sẽ đọc lại nội dung từ tệp hocsinh.txt vừa được tạo ở trên và in ra màn hình.

# Mở tệp 'hocsinh.txt' ở chế độ đọc ('r')
try:
    with open('hocsinh.txt', 'r', encoding='utf-8') as f:
        # Đọc toàn bộ nội dung tệp vào một chuỗi duy nhất
        # noi_dung = f.read() 
        # print("Nội dung đọc được từ file (dạng chuỗi):")
        # print(noi_dung)

        # Hoặc đọc nội dung tệp theo từng dòng, lưu vào một danh sách
        cac_dong = f.readlines()
        print("Nội dung đọc được từ file (dạng danh sách các dòng):")
        for dong in cac_dong:
            # In mỗi dòng và loại bỏ ký tự xuống dòng thừa bằng .strip()
            print(dong.strip())
            
except FileNotFoundError:
    print("Lỗi: Không tìm thấy tệp hocsinh.txt.")

Phân tích:

  • Chương trình sử dụng chế độ 'r' để mở tệp chỉ để đọc.
  • Phương thức f.readlines() đọc tất cả các dòng trong tệp và trả về một danh sách (list), mỗi phần tử là một dòng (bao gồm cả ký tự n ở cuối).
  • Phương thức .strip() được dùng để loại bỏ các khoảng trắng hoặc ký tự đặc biệt (như n) ở đầu và cuối mỗi chuỗi trước khi in ra.
  • Khối try...except FileNotFoundError được dùng để xử lý trường hợp tệp không tồn tại, giúp chương trình không bị dừng đột ngột.

4. Trực quan hóa và gỡ lỗi với Thonny

Môi trường lập trình Thonny cung cấp một công cụ hữu ích để quan sát sự thay đổi của tệp tin. Cửa sổ “Files” của Thonny cho phép người dùng xem cây thư mục và các tệp tin hiện có.

Khi chạy đoạn mã trong Ví dụ 1, người dùng có thể quan sát các bước sau:

  1. Trước khi chạy, tệp hocsinh.txt không tồn tại trong danh sách tệp.
  2. Sau khi thực thi câu lệnh with open(…), tệp hocsinh.txt sẽ ngay lập tức xuất hiện trong cửa sổ “Files”.
  3. Người dùng có thể nháy đúp chuột vào tệp hocsinh.txt để mở và kiểm tra nội dung vừa được ghi, xác nhận rằng chương trình đã hoạt động chính xác.

5. Bài tập vận dụng

5.1. Bài tập 1

5.1.1. Đề bài

Viết một chương trình Python thực hiện các yêu cầu sau:

  1. Tạo một danh sách (list) chứa 5 số nguyên bất kỳ.
  2. Mở một tệp có tên SONGUYEN.OUT ở chế độ ghi.
  3. Ghi 5 số nguyên này vào tệp, mỗi số trên một dòng riêng biệt.

5.1.2. Lời giải và phân tích

# 1. Tạo một danh sách chứa 5 số nguyên
danh_sach_so = [10, 25, 3, 48, 100]

# Tên tệp đầu ra
ten_tep_ra = 'SONGUYEN.OUT'

# 2. Mở tệp ở chế độ ghi
try:
    with open(ten_tep_ra, 'w', encoding='utf-8') as f:
        # 3. Ghi từng số vào tệp
        for so in danh_sach_so:
            # Chuyển số nguyên thành chuỗi và thêm ký tự xuống dòng
            f.write(str(so) + 'n')
    
    print(f"Đã ghi thành công các số vào tệp {ten_tep_ra}")

except IOError:
    print(f"Lỗi: Không thể ghi vào tệp {ten_tep_ra}")

Phân tích:

  • Bài toán yêu cầu ghi số nguyên ra tệp. Phương thức .write() chỉ chấp nhận tham số là chuỗi (string). Do đó, mỗi số nguyên so phải được chuyển đổi thành chuỗi bằng hàm str(so) trước khi ghi.
  • Tương tự Ví dụ 1, ký tự ‘n’ được nối vào sau mỗi số để đảm bảo định dạng mỗi số một dòng.
  • Sử dụng khối try…except IOError là một cách thực hành tốt để bắt các lỗi có thể xảy ra trong quá trình ghi tệp.

5.2. Bài tập 2

5.2.1. Đề bài

Theo định dạng của các kỳ thi Tin học trẻ, một bài toán tính tổng hai số nguyên sẽ có dữ liệu đầu vào và đầu ra như sau:

  • Đầu vào: Một tệp TONG.INP chứa hai số nguyên a và b, mỗi số nằm trên một dòng.
  • Đầu ra: Ghi tổng của a và b vào tệp TONG.OUT.

Ví dụ:
Nếu tệp TONG.INP có nội dung:

15
20

Thì tệp TONG.OUT phải có nội dung:

35

Hãy viết chương trình Python để giải bài toán trên.

5.2.2. Lời giải và phân tích

# Định nghĩa tên tệp vào và ra
tep_vao = 'TONG.INP'
tep_ra = 'TONG.OUT'

try:
    # Bước 1: Đọc dữ liệu từ tệp TONG.INP
    with open(tep_vao, 'r', encoding='utf-8') as f_in:
        # Đọc dòng đầu tiên, chuyển thành số nguyên
        a = int(f_in.readline())
        # Đọc dòng thứ hai, chuyển thành số nguyên
        b = int(f_in.readline())

    # Bước 2: Tính toán
    tong = a + b

    # Bước 3: Ghi kết quả ra tệp TONG.OUT
    with open(tep_ra, 'w', encoding='utf-8') as f_out:
        # Chuyển kết quả (số nguyên) thành chuỗi trước khi ghi
        f_out.write(str(tong))

    print(f"Đã thực hiện xong. Kết quả được lưu trong tệp {tep_ra}")

except FileNotFoundError:
    print(f"Lỗi: Không tìm thấy tệp đầu vào {tep_vao}.")
except ValueError:
    print(f"Lỗi: Dữ liệu trong tệp {tep_vao} không phải là số nguyên.")
except Exception as e:
    print(f"Đã xảy ra một lỗi không xác định: {e}")

Phân tích:

  • Chương trình được chia thành 3 bước rõ ràng: Đọc dữ liệu, xử lý, và ghi kết quả.
  • Sử dụng f_in.readline() để đọc từng dòng một. Dữ liệu đọc từ tệp văn bản luôn ở dạng chuỗi, do đó cần dùng hàm int() để chuyển đổi chúng thành số nguyên trước khi thực hiện phép cộng.
  • Kết quả tong là một số nguyên. Nó phải được chuyển đổi ngược lại thành chuỗi bằng str(tong) trước khi có thể ghi ra tệp TONG.OUT bằng phương thức .write().
  • Chương trình này mô phỏng chính xác quy trình làm bài trong một kỳ thi lập trình tiêu chuẩn.

6. Các lưu ý và lỗi thường gặp

  • FileNotFoundError: Lỗi này xảy ra khi cố gắng mở một tệp ở chế độ đọc (‘r’) nhưng tệp đó không tồn tại trong thư mục làm việc. Luôn đảm bảo tệp đầu vào có tồn tại và đúng tên.
  • Chế độ ‘w’ sẽ xóa nội dung cũ: Khi mở một tệp đã tồn tại bằng chế độ ‘w’, toàn bộ nội dung cũ của tệp sẽ bị xóa mà KHÔNG có cảnh báo. Cần cẩn trọng để không làm mất dữ liệu quan trọng.
  • Quên chuyển đổi kiểu dữ liệu: Dữ liệu đọc từ tệp luôn là chuỗi. Phải dùng int(), float() để chuyển đổi sang kiểu số nếu cần tính toán. Ngược lại, dữ liệu ghi vào tệp phải là chuỗi, nên cần dùng str() để chuyển đổi các kiểu dữ liệu khác.
  • Vấn đề về đường dẫn (Path): Nếu chỉ dùng tên tệp (ví dụ: ‘data.txt’), Python sẽ tìm/tạo tệp trong cùng thư mục với tệp mã nguồn. Nếu tệp nằm ở vị trí khác, cần phải cung cấp đường dẫn đầy đủ.
  • Lỗi mã hóa (Encoding Error): Nếu không chỉ định encoding='utf-8', chương trình có thể gặp lỗi khi đọc hoặc ghi các ký tự không thuộc bảng mã ASCII, ví dụ như tiếng Việt có dấu.

7. Tổng kết

Bài học đã giới thiệu về các thao tác cơ bản và thiết yếu với tệp văn bản trong Python. Việc sử dụng hàm open() kết hợp với câu lệnh with là phương pháp chuẩn và an toàn để đọc (‘r’) và ghi (‘w’) dữ liệu. Kỹ năng này không chỉ là một phần quan trọng của ngôn ngữ Python mà còn là một yêu cầu bắt buộc trong môi trường lập trình thi đấu, nơi dữ liệu vào/ra thường được xử lý qua tệp.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *