Python cơ bản: đọc ghi file, pathlib và xử lý lỗi
Đọc ghi file UTF-8 với with, đường dẫn bằng pathlib, try/except đúng cách và hàm đọc cấu hình JSON an toàn.
Học xong bài này, bạn sẽ
- Đọc ghi file văn bản với with
- Làm việc với đường dẫn bằng pathlib
- Bắt lỗi bằng try/except đúng cách
Nên học trước: Hàm và module
Nội dung bài viết
Chương trình thật phải đọc cấu hình từ file, ghi kết quả ra file, và không được "chết" chỉ vì một ảnh hỏng hay một file thiếu. Bài này dạy ba kỹ năng đó: đọc ghi file văn bản, làm việc với đường dẫn bằng pathlib, và xử lý lỗi bằng try/except. Cuối bài bạn sẽ có hàm đọc file cấu hình JSON an toàn, thứ mọi trạm vision đều cần.
1. Đọc và ghi file văn bản với with
with open("ket_qua_ca.txt", "w", encoding="utf-8") as f:
f.write("SP-01,OK\n")
f.write("SP-02,NG,xước\n")
with open("ket_qua_ca.txt", "a", encoding="utf-8") as f: # "a": ghi thêm vào cuối
f.write("SP-03,OK\n")
with open("ket_qua_ca.txt", encoding="utf-8") as f:
for dong in f:
print(dong.strip().split(","))
['SP-01', 'OK']
['SP-02', 'NG', 'xước']
['SP-03', 'OK']
- Chế độ mở:
"r"đọc (mặc định),"w"ghi mới (xóa nội dung cũ),"a"ghi thêm. withtự đóng file khi xong, kể cả khi có lỗi. Luôn dùngwiththay vìopen()rồi quênclose().- Luôn ghi
encoding="utf-8". Trên Windows, mặc định có thể không phải UTF-8 và chữ tiếng Việt sẽ bị lỗi. "\n"là ký tự xuống dòng;.strip()bỏ nó khi đọc.
2. pathlib: làm việc với đường dẫn
Đừng ghép đường dẫn bằng cộng chuỗi ("anh" + "\\" + ten): sai ngay khi chuyển từ Windows sang Linux. Dùng pathlib.Path và toán tử /:
from pathlib import Path
goc = Path("du_lieu")
thu_muc_ng = goc / "2026-10-09" / "NG"
thu_muc_ng.mkdir(parents=True, exist_ok=True) # tạo cả cây thư mục, không lỗi nếu đã có
file_anh = thu_muc_ng / "ST3_000128_NG.png"
print(file_anh) # du_lieu/2026-10-09/NG/ST3_000128_NG.png (Windows hiện dấu \)
print(file_anh.name) # ST3_000128_NG.png
print(file_anh.stem) # ST3_000128_NG
print(file_anh.suffix) # .png
print(file_anh.parent.name) # NG
print(file_anh.exists()) # False (chưa tạo file)
print(file_anh.with_suffix(".jpg").name) # ST3_000128_NG.jpg
Liệt kê file
for i in range(3):
(thu_muc_ng / f"anh_{i}.png").write_text("demo") # tạo vài file thử
ds_anh = sorted(thu_muc_ng.glob("*.png")) # glob: tìm theo mẫu tên
print(len(ds_anh), [p.name for p in ds_anh])
tat_ca = list(goc.rglob("*.png")) # rglob: tìm cả thư mục con
print(len(tat_ca))
3 ['anh_0.png', 'anh_1.png', 'anh_2.png']
3
Path.read_text() và Path.write_text() là cách ngắn gọn để đọc ghi cả file văn bản một lần (cũng nên truyền encoding="utf-8" khi có tiếng Việt).
3. Ngoại lệ: khi có lỗi lúc chạy
Khi gặp lỗi, Python "ném" ra một ngoại lệ (exception). Không ai bắt thì chương trình dừng. Với trạm kiểm tra chạy 24/7, một file ảnh hỏng không được phép làm dừng cả dây chuyền. try/except bắt ngoại lệ và xử lý:
gia_tri_doc = ["12.48", "12,51", "", "12.50"] # dữ liệu "bẩn" từ file
ket_qua = []
for s in gia_tri_doc:
try:
ket_qua.append(float(s))
except ValueError:
print(f"Bỏ qua giá trị không hợp lệ: {s!r}")
print(ket_qua)
Bỏ qua giá trị không hợp lệ: '12,51'
Bỏ qua giá trị không hợp lệ: ''
[12.48, 12.5]
{s!r} in chuỗi kèm dấu nháy để thấy rõ cả chuỗi rỗng. Thực tế bạn có thể sửa "12,51" thành "12.51" bằng .replace(",", ".") trước khi ép kiểu.
Đầy đủ: try / except / else / finally
from pathlib import Path
def doc_so_luong(duong_dan):
try:
with open(duong_dan, encoding="utf-8") as f:
n = int(f.read().strip())
except FileNotFoundError:
print("Không thấy file:", duong_dan)
return 0
except ValueError:
print("Nội dung file không phải số nguyên")
return 0
else:
print("Đọc thành công") # chỉ chạy khi không có lỗi
return n
finally:
print("Kết thúc đọc file") # luôn chạy, có lỗi hay không
Path("dem.txt").write_text("1284", encoding="utf-8")
print(doc_so_luong("dem.txt"))
print(doc_so_luong("khong_co.txt"))
Đọc thành công
Kết thúc đọc file
1284
Không thấy file: khong_co.txt
Kết thúc đọc file
0
Các ngoại lệ hay gặp
| Ngoại lệ | Khi nào |
|---|---|
FileNotFoundError | Mở file không tồn tại |
ValueError | Giá trị sai dạng: int("abc") |
KeyError | Khóa không có trong dict |
IndexError | Chỉ số vượt quá độ dài list |
ZeroDivisionError | Chia cho 0 |
PermissionError | Không có quyền ghi (file đang mở trong Excel…) |
except: trống hoặc except Exception: pass sẽ che mất lỗi thật, khiến bạn không biết vì sao chương trình chạy sai. Chỉ bắt đúng loại lỗi bạn biết cách xử lý, và luôn ghi lại (in hoặc log) lỗi đó.Tự ném lỗi với raise
def kiem_tra_nguong(nguong: int) -> int:
if not 0 <= nguong <= 255:
raise ValueError(f"Ngưỡng phải trong khoảng 0–255, nhận {nguong}")
return nguong
try:
kiem_tra_nguong(300)
except ValueError as loi:
print("Cấu hình sai:", loi)
Cấu hình sai: Ngưỡng phải trong khoảng 0–255, nhận 300
Báo lỗi sớm với thông điệp rõ ràng tốt hơn nhiều so với để chương trình chạy tiếp với giá trị sai rồi cho ra kết quả kiểm tra sai.
4. Ứng dụng: file cấu hình JSON
Tham số thuật toán (ngưỡng, vùng ROI, dung sai) nên nằm trong file cấu hình để người vận hành chỉnh mà không phải sửa code. JSON là định dạng phổ biến nhất, và module json chuyển qua lại giữa file JSON và dict:
import json
from pathlib import Path
MAC_DINH = {"nguong": 128, "dien_tich_min": 500, "roi": [100, 80, 400, 300], "luu_anh_ok": False}
def doc_cau_hinh(duong_dan: str | Path) -> dict:
"""Đọc file cấu hình JSON; thiếu file hoặc thiếu khóa thì dùng giá trị mặc định."""
duong_dan = Path(duong_dan)
cau_hinh = dict(MAC_DINH)
if not duong_dan.exists():
print(f"Chưa có {duong_dan}, tạo file mặc định")
duong_dan.write_text(json.dumps(MAC_DINH, indent=2, ensure_ascii=False), encoding="utf-8")
return cau_hinh
try:
cau_hinh.update(json.loads(duong_dan.read_text(encoding="utf-8")))
except json.JSONDecodeError as loi:
raise ValueError(f"File {duong_dan} sai cú pháp JSON: {loi}") from loi
if not 0 <= cau_hinh["nguong"] <= 255:
raise ValueError("nguong phải trong khoảng 0–255")
return cau_hinh
cfg = doc_cau_hinh("cau_hinh.json") # lần đầu: tạo file mặc định
cfg = doc_cau_hinh("cau_hinh.json") # lần sau: đọc từ file
print(cfg["nguong"], cfg["roi"])
print(Path("cau_hinh.json").read_text(encoding="utf-8"))
Chưa có cau_hinh.json, tạo file mặc định
128 [100, 80, 400, 300]
{
"nguong": 128,
"dien_tich_min": 500,
"roi": [
100,
80,
400,
300
],
"luu_anh_ok": false
}
Ba chi tiết đáng học:
ensure_ascii=Falsegiữ chữ tiếng Việt dễ đọc trong file JSON.- Bắt đầu từ giá trị mặc định rồi
updatebằng nội dung file: file thiếu khóa nào thì vẫn có giá trị dùng được. - Kiểm tra giá trị ngay sau khi đọc và báo lỗi rõ ràng (
raise ... from loigiữ lại lỗi gốc để dễ tra).
JSON chỉ có list, không có tuple: (100, 80, 400, 300) lưu xuống rồi đọc lên sẽ thành list [100, 80, 400, 300].
Tóm tắt
with open(..., encoding="utf-8");"w"ghi đè,"a"ghi thêm.Path+/để ghép đường dẫn;mkdir(parents=True, exist_ok=True);glob,rglob.try/exceptbắt đúng loại lỗi;finallyluôn chạy;raisebáo lỗi sớm.- Cấu hình để trong JSON, có mặc định và kiểm tra giá trị.
Bài tập
Bài 1. Đọc file kết quả an toàn
File do_luong.txt mỗi dòng một giá trị đo, có dòng trống, dòng dùng dấu phẩy thập phân và dòng chữ. Viết hàm trả về list số thực hợp lệ (chấp nhận cả dấu phẩy) và số dòng bị bỏ qua.
Xem đáp án
from pathlib import Path
Path("do_luong.txt").write_text("12.48\n12,51\n\nabc\n12.50\n", encoding="utf-8")
def doc_gia_tri(duong_dan):
hop_le, bo_qua = [], 0
for dong in Path(duong_dan).read_text(encoding="utf-8").splitlines():
dong = dong.strip().replace(",", ".")
if not dong:
continue
try:
hop_le.append(float(dong))
except ValueError:
bo_qua += 1
return hop_le, bo_qua
print(doc_gia_tri("do_luong.txt"))
([12.48, 12.51, 12.5], 1)
Bài 2. Sắp xếp ảnh theo kết quả
Thư mục anh_moi chứa ảnh tên dạng ST1_000001_OK.png, ST1_000002_NG.png. Viết chương trình chuyển ảnh vào da_phan_loai/OK hoặc da_phan_loai/NG dựa vào phần cuối tên file. Gợi ý: Path.rename() hoặc shutil.move().
Xem đáp án
from pathlib import Path
import shutil
nguon = Path("anh_moi")
nguon.mkdir(exist_ok=True)
for i, kq in enumerate(["OK", "NG", "OK"], start=1):
(nguon / f"ST1_{i:06d}_{kq}.png").write_text("demo")
dich = Path("da_phan_loai")
for anh in nguon.glob("*.png"):
ket_qua = anh.stem.split("_")[-1] # phần cuối tên: OK hoặc NG
thu_muc = dich / ket_qua
thu_muc.mkdir(parents=True, exist_ok=True)
shutil.move(str(anh), str(thu_muc / anh.name))
for p in sorted(dich.rglob("*.png")):
print(p.parent.name, p.name)
NG ST1_000002_NG.png
OK ST1_000001_OK.png
OK ST1_000003_OK.png
Bài 3. Kiểm tra cấu hình đầy đủ hơn
Mở rộng hàm doc_cau_hinh: báo lỗi nếu roi không có đúng 4 số, hoặc chiều rộng/cao của ROI (phần tử thứ 3, 4) không dương. Thử với file JSON sai để thấy thông báo lỗi.
Gợi ý
Thêm sau dòng kiểm tra ngưỡng: if len(cau_hinh["roi"]) != 4 or cau_hinh["roi"][2] <= 0 or cau_hinh["roi"][3] <= 0: raise ValueError(...). Tạo file sai bằng Path("sai.json").write_text('{"roi": [1, 2, 3]}').
Bài tiếp theo: bạn sẽ tạo môi trường ảo và cài thư viện bằng pip, chuẩn bị cho NumPy và OpenCV.


