Câu 1Tham số thứ hai "r" trong with open("data/notes.txt", "r") as f: nghĩa là gì?
File I/O — Đọc và ghi an toàn với with open()
Học file I/O trong Python. with open() tự đóng, khác biệt giữa read / readlines / readline, mode w / a, và pattern đọc-xử lý-ghi lưu sang file riêng — kèm thực hành.
Phần lớn dữ liệu mà chương trình xử lý sống trên đĩa dưới dạng file. Đọc một danh sách công việc, append vào một log, load một config — tất cả đều là thao tác hằng ngày.
Trong Python, with open() cho phép bạn làm điều này trong một đơn vị cú pháp an toàn. Bài này bao quát dạng cơ bản của with open(), ba phương thức đọc (read / readlines / readline), ghi (w) và append (a), và xử lý đường dẫn có thư mục — bằng cách thực sự chạy code với file.
with open() mở và đóng file an toàn
Mở file là thao tác khiến OS giữ một resource, và một khi đã mở, bạn phải đóng. Quên là gây cạn file descriptor, hoặc dữ liệu ghi kẹt trong buffer và không bao giờ tới đĩa.
open() khiến OS cấp phát một file descriptor (FD). Cho tới close(), FD vẫn bị giữ và writes vẫn buffered, chưa flush. Quên đóng là rò rỉ resource nghiêm trọng.File descriptor (FD) là gì?
FD (file descriptor) là ID số nguyên mà OS gán cho mỗi file đang mở. Mỗi open() cấp một FD mới; close() trả nó về OS. OS đặt giới hạn trên mỗi process (mặc định Linux khoảng 1.024). Vượt giới hạn đó và các lời gọi open() mới bắt đầu thất bại với OSError: Too many open files. Mối nguy của quên đóng chính là: giữ một resource hữu hạn mãi mãi.
Viết dạng with open(path, mode) as f: và file tự đóng ngay khi bạn rời block, nên không thể quên. f trong as f là biến nhận object file; bên trong block with, bạn đọc và ghi qua f.
Đường dẫn dùng dấu gạch chéo / để tách thư mục. open("data/tasks.txt", "r") mở tasks.txt bên trong thư mục data dưới thư mục hiện tại. Ngay cả trên Windows, quy ước Python dùng / — bên trong, nó được dịch theo OS.
with mở file và bind nó với as f. Rời block — bình thường hoặc qua exception — close() chạy tự động.Đọc — read / readlines / readline
Để đọc file, dùng r (read) cho tham số thứ hai của open(path, "r"). Object file trả về có ba phương thức đọc cho các nhu cầu khác nhau.
- f.read() — đọc toàn bộ file thành một chuỗi.
- f.readlines() — đọc thành list các dòng (chuỗi).
- f.readline() — đọc một dòng. Lời gọi lặp lại tiến tới dòng kế; chuỗi rỗng "" báo hiệu end of file.
# Lấy toàn bộ file thành một chuỗi
with open("data/tasks.txt", "r") as f:
content = f.read()
print(type(content)) # <class 'str'>
# Lấy list các dòng
with open("data/tasks.txt", "r") as f:
lines = f.readlines()
print(type(lines)) # <class 'list'>
# Lấy một dòng tại một thời điểm (gọi tiếp -> dòng kế)
with open("data/tasks.txt", "r") as f:
first = f.readline() # "Viết báo cáo\n"
second = f.readline() # "Trả lời email\n"
Từ đây trở đi, console chạy trên file system ảo (VFS) phía trình duyệt
Console bên phải là file system ảo trong trình duyệt — các file như data/tasks.txt đã được dàn sẵn cho bạn. Để chạy cùng code trên PC của bạn, tạo thư mục data/ cạnh file Python của bạn (ví dụ main.py) và đặt tasks.txt trong đó trước khi chạy. Cú pháp đường dẫn ("data/tasks.txt" với /) giống hệt Python thật.
Khi file là một bản ghi mỗi dòng (CSV không header, list cách nhau bởi newline, log lines), f.readlines() tiện. Nó trả list các chuỗi, mỗi cái một dòng, và mỗi phần tử giữ \n ở cuối.
In nó trực tiếp tạo cách dòng đôi, nên pattern chuẩn là dùng .rstrip("\n") để bỏ newline cuối.
File lớn? Đọc từng dòng — readline và walrus
f.read() và f.readlines() load toàn file vào memory. Tốt cho file cỡ config, nhưng một log server nhiều GB có thể crash chương trình do hết memory.
f.readline() load chỉ một dòng tại một thời điểm, nên dùng memory chỉ ở mức một dòng. Khi readline() tới end of file, nó trả chuỗi rỗng "", mà bạn dùng làm điều kiện thoát vòng lặp.
"" để báo vòng lặp dừng.# Phong cách kinh điển: break trên chuỗi rỗng
with open("data/log.txt", "r") as f:
while True:
line = f.readline()
if not line: # chuỗi rỗng = EOF
break
print(line.rstrip())
Toán tử walrus := viết tương tự với ít hơn một dòng
Python 3.8 giới thiệu toán tử walrus (:=), cho phép bạn gán và kiểm tra trong một biểu thức. Viết while line := f.readline(): nghĩa là «đặt kết quả của readline() vào line, và tiếp tục lặp khi nó còn không rỗng» — toàn vòng lặp trong một dòng.
Ghi — mode w và mode a
Ghi chỉ là open() với tham số thứ hai khác. Hai mode mang phần lớn trọng lượng thực tế.
- "w" (write) — tạo mới hoặc ghi đè. Nội dung cũ bị phá hủy.
- "a" (append) — append vào cuối. Nội dung cũ giữ nguyên.
Cũng có hai phương thức ghi. f.write(s) ghi chuỗi s trực tiếp; f.writelines(lst) ghi list các chuỗi trong một lần. Nếu đường dẫn bao gồm thư mục, file được tạo trong đó (thư mục phải đã tồn tại).
# mode w: ghi từng chuỗi một
with open("data/done.txt", "w") as f:
f.write("Viết báo cáo\n")
f.write("Trả lời email\n")
# mode w: ghi list cùng lúc với writelines
with open("data/done.txt", "w") as f:
f.writelines(["Viết báo cáo\n", "Trả lời email\n", "Dự họp\n"])
# mode a: append vào cuối
with open("data/done.txt", "a") as f:
f.write("Đi mua sắm\n")
Newline \n không được thêm tự động
Khác với print(), cả f.write() và f.writelines() không thêm newline cho bạn. Gọi f.write("Hello") hai lần cho bạn HelloHello trong file. Chèn \n rõ ràng ở bất cứ đâu bạn thực sự muốn xuống dòng.
Đi xa hơn — Phân tích file và lưu kết quả sang file khác
Một trong những pattern phổ biến nhất trong công việc thực là đọc file input, làm phân tích, và ghi kết quả sang file riêng. Chỉ cần dùng open() hai lần — một với "r", một với "w". Phong cách sạch nhất là đặt input → process → output vào ba block with riêng.
# Phân tích data/log.txt và lưu kết quả sang data/log_summary.txt
# (1) Input: đọc log thành list các dòng
with open("data/log.txt", "r") as src:
lines = src.readlines()
# (2) Process: đếm dòng, lấy entry đầu/cuối
total = len(lines)
first = lines[0].rstrip()
last = lines[-1].rstrip()
summary = f"count: {total}\nfirst: {first}\nlast: {last}\n"
# (3) Output: ghi sang file khác
with open("data/log_summary.txt", "w") as dst:
dst.write(summary)
print("Saved analysis to data/log_summary.txt")
encoding="utf-8" là chuẩn cho mã hóa text
Trên Python thật, bạn thường viết open(path, "r", encoding="utf-8") để chỉ định encoding rõ ràng. Cố đọc file Shift-JIS dưới dạng UTF-8 báo UnicodeDecodeError, và ngược lại tạo mojibake im lặng. Chuẩn thế giới là UTF-8 — viết file mới ở UTF-8 mặc định. Môi trường trình duyệt hard-code UTF-8 bên trong nên bỏ encoding= chạy được ở đây, nhưng hãy biến thành thói quen khi viết script local.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Khi bạn gọi f.readline() lặp lại và file kết thúc, giá trị nào được trả về?
Câu 3Nếu data/done.txt đã có nội dung và bạn mở với with open("data/done.txt", "w") as f:, chuyện gì xảy ra với nội dung cũ?