Đến giờ, mọi dữ liệu bạn xử lý đều biến mất khi chương trình kết thúc. File cho phép:
Lưu dữ liệu lâu dài (persistence)
Đọc dữ liệu từ nguồn bên ngoài (Excel xuất CSV, log hệ thống, file cấu hình)
Xuất báo cáo để gửi cho người khác
2. open() và with
2.1. Cách cũ (KHÔNG khuyến nghị)
f = open("du_lieu.txt", "r", encoding="utf-8")noi_dung = f.read()f.close() # BẮT BUỘC phải đóngprint(noi_dung)
Vấn đề: nếu có lỗi xảy ra giữa open và close, file không được đóng → khóa file, rò rỉ tài nguyên.
2.2. Cách chuẩn — with (KHUYẾN NGHỊ)
with open("du_lieu.txt", "r", encoding="utf-8") as f: noi_dung = f.read() print(noi_dung)# File TỰ ĐỘNG đóng ở đây, kể cả khi có lỗi
with là context manager (trình quản lý ngữ cảnh) — tự động dọn dẹp tài nguyên khi ra khỏi khối lệnh.
Quy tắc
Luôn dùng with khi mở file. Không có ngoại lệ nào đáng kể cho người mới.
3. Chế độ mở file (mode)
Mode
Tên
Tác dụng
File chưa tồn tại
"r"
read
Đọc (mặc định)
❌ FileNotFoundError
"w"
write
Ghi — XÓA SẠCH nội dung cũ
✅ Tạo mới
"a"
append
Ghi thêm vào cuối file
✅ Tạo mới
"x"
exclusive
Tạo mới — lỗi nếu file đã có
✅ Tạo mới
"r+"
read/write
Vừa đọc vừa ghi
❌ Lỗi
Thêm "b" để làm việc với file nhị phân: "rb", "wb" (ảnh, PDF, Excel…).
Mode "w" XÓA SẠCH file cũ
with open("quan_trong.txt", "w", encoding="utf-8") as f: f.write("abc")
Nếu quan_trong.txt có 10.000 dòng dữ liệu, sau lệnh này chỉ còn abc. Không có Undo.
Muốn thêm dữ liệu vào file có sẵn → dùng "a".
4. ⚠️ Encoding — Bảng mã (QUAN TRỌNG VỚI TIẾNG VIỆT)
4.1. Luôn chỉ định encoding
# ❌ SAI — dùng encoding mặc định của hệ điều hànhwith open("du_lieu.txt", "r") as f: ...# ✅ ĐÚNGwith open("du_lieu.txt", "r", encoding="utf-8") as f: ...
Trên Windows tiếng Việt, encoding mặc định thường là cp1258 hoặc cp1252, không phải UTF-8. Đọc file UTF-8 mà không chỉ định sẽ gây:
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 15
Hoặc tệ hơn: đọc được nhưng chữ bị lỗi thành Nguy?n V?n A hoặc Nguyên Văn A.
4.2. Các encoding thường gặp
Encoding
Dùng khi
utf-8
✅ Mặc định nên dùng cho mọi trường hợp
utf-8-sig
File CSV do Excel tạo (có BOM ở đầu file)
cp1258
File cũ tạo trên Windows tiếng Việt
latin-1
Đọc “cứu hộ” — không bao giờ lỗi nhưng có thể sai chữ
BOM và Excel
Excel khi lưu file CSV thường thêm 3 byte đặc biệt ở đầu (BOM — Byte Order Mark). Nếu đọc bằng utf-8, ký tự đầu tiên sẽ thành \ufeff:
# ❌ Cột đầu tiên bị lỗi tênwith open("tu_excel.csv", encoding="utf-8") as f: ...# ✅ Tự động bỏ BOMwith open("tu_excel.csv", encoding="utf-8-sig") as f: ...
Ngược lại, khi ghi file CSV để mở bằng Excel, dùng encoding="utf-8-sig" để Excel hiển thị đúng tiếng Việt.
4.3. Xử lý lỗi encoding
# Bỏ qua ký tự lỗiwith open("file.txt", encoding="utf-8", errors="ignore") as f: ...# Thay ký tự lỗi bằng dấu ?with open("file.txt", encoding="utf-8", errors="replace") as f: ...
Chỉ dùng khi bắt buộc — vì bạn đang mất dữ liệu.
5. Đọc file văn bản
Giả sử có file khach_hang.txt:
KH001|Nguyễn Văn A|Huế|5000000
KH002|Trần Thị B|TP HCM|12000000
KH003|Lê Văn C|Hà Nội|0
5.1. read() — Đọc toàn bộ thành một chuỗi
with open("khach_hang.txt", encoding="utf-8") as f: noi_dung = f.read()print(noi_dung)print(type(noi_dung)) # <class 'str'>
⚠️ Không dùng với file lớn (hàng GB) vì nạp hết vào RAM.
5.2. readlines() — Đọc thành list các dòng
with open("khach_hang.txt", encoding="utf-8") as f: cac_dong = f.readlines()print(cac_dong)# ['KH001|Nguyễn Văn A|Huế|5000000\n', 'KH002|...\n', ...]
Chú ý: mỗi dòng còn ký tự xuống dòng \n ở cuối.
5.3. Duyệt từng dòng (KHUYẾN NGHỊ)
with open("khach_hang.txt", encoding="utf-8") as f: for dong in f: print(dong.strip()) # strip() bỏ ký tự \n
Cách này tiết kiệm bộ nhớ nhất — đọc từng dòng một, không nạp hết vào RAM. Dùng được với file cực lớn.
5.4. Ví dụ đầy đủ — Đọc và phân tích
from pathlib import PathDUONG_DAN = Path("khach_hang.txt")def doc_khach_hang(duong_dan): """ Đọc file khách hàng, trả về list các dict. Định dạng file: MaKH|TenKH|Tinh|CongNo (phân cách bằng |) Trả về: tuple: (danh sách khách hàng, danh sách lỗi) """ khach_hang = [] loi = [] try: with open(duong_dan, encoding="utf-8") as f: for so_dong, dong in enumerate(f, start=1): dong = dong.strip() if not dong: # Bỏ qua dòng trống continue if dong.startswith("#"): # Bỏ qua dòng comment continue phan = dong.split("|") if len(phan) != 4: loi.append(f"Dong {so_dong}: sai so cot ({len(phan)}/4)") continue try: cong_no = float(phan[3]) except ValueError: loi.append(f"Dong {so_dong}: cong no khong phai so: '{phan[3]}'") continue khach_hang.append({ "ma": phan[0].strip(), "ten": phan[1].strip(), "tinh": phan[2].strip(), "cong_no": cong_no, }) except FileNotFoundError: print(f"Loi: Khong tim thay file '{duong_dan}'") except PermissionError: print(f"Loi: Khong co quyen doc file '{duong_dan}'") except UnicodeDecodeError as e: print(f"Loi bang ma khi doc file: {e}") return khach_hang, loids, cac_loi = doc_khach_hang(DUONG_DAN)print(f"Doc thanh cong {len(ds)} ban ghi, {len(cac_loi)} dong loi")print()if cac_loi: print("CHI TIET LOI:") for l in cac_loi: print(f" - {l}") print()print(f"{'MA':<8}{'TEN KHACH HANG':<22}{'TINH':<12}{'CONG NO':>15}")print("-" * 57)for kh in ds: print(f"{kh['ma']:<8}{kh['ten']:<22}{kh['tinh']:<12}{kh['cong_no']:>15,.0f}")print("-" * 57)print(f"{'TONG CONG':<42}{sum(k['cong_no'] for k in ds):>15,.0f}")
6. Ghi file văn bản
6.1. write() — Ghi một chuỗi
with open("bao_cao.txt", "w", encoding="utf-8") as f: f.write("BAO CAO CONG NO\n") f.write("=" * 40 + "\n") f.write("Ngay lap: 08/08/2026\n")
from datetime import datetimedef ghi_log(thong_diep, duong_dan="he_thong.log"): """Ghi một dòng log kèm thời gian.""" thoi_gian = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(duong_dan, "a", encoding="utf-8") as f: f.write(f"[{thoi_gian}] {thong_diep}\n")ghi_log("Bat dau chuong trinh")ghi_log("Doc file khach hang thanh cong: 150 ban ghi")ghi_log("LOI: Khong ket noi duoc database")
6.4. Xuất báo cáo ra file
def xuat_bao_cao(khach_hang, duong_dan="bao_cao_cong_no.txt"): """Xuất báo cáo công nợ ra file văn bản.""" W = 60 tong = sum(k["cong_no"] for k in khach_hang) with open(duong_dan, "w", encoding="utf-8") as f: f.write("=" * W + "\n") f.write("BAO CAO CONG NO KHACH HANG".center(W) + "\n") f.write(f"Ngay lap: {datetime.now():%d/%m/%Y %H:%M}".center(W) + "\n") f.write("=" * W + "\n") f.write(f"{'MA':<8}{'TEN KHACH HANG':<25}{'CONG NO':>27}\n") f.write("-" * W + "\n") for kh in sorted(khach_hang, key=lambda k: k["cong_no"], reverse=True): f.write(f"{kh['ma']:<8}{kh['ten']:<25}{kh['cong_no']:>27,.0f}\n") f.write("-" * W + "\n") f.write(f"{'TONG CONG':<33}{tong:>27,.0f}\n") f.write("=" * W + "\n") print(f"Da xuat bao cao ra file: {duong_dan}")
PHẦN 2 — TUẦN 21
7. File CSV
CSV = Comma-Separated Values (giá trị phân cách bằng dấu phẩy) — định dạng phổ biến nhất để trao đổi dữ liệu bảng, đọc được bằng Excel.
7.1. Vì sao không tự split(",")?
# ❌ Cách thủ công — SAI với dữ liệu thực tếdong = 'KH001,"Cong ty ABC, Chi nhanh Hue",5000000'print(dong.split(","))# ['KH001', '"Cong ty ABC', ' Chi nhanh Hue"', '5000000'] <- SAI! bị tách 4 phần
Dữ liệu thật thường có dấu phẩy bên trong dấu nháy kép. Module csv xử lý đúng các trường hợp này.
7.2. Đọc CSV — csv.reader
File hang_hoa.csv:
ma,ten,don_vi,so_luong,don_gia
VT001,Bàn làm việc,Cái,3,2500000
VT002,"Ghế xoay, có tựa đầu",Cái,10,850000
VT003,Tủ hồ sơ,Cái,2,4200000
import csvwith open("hang_hoa.csv", newline="", encoding="utf-8-sig") as f: doc = csv.reader(f) tieu_de = next(doc) # Đọc dòng đầu (header) print("Cot:", tieu_de) for dong in doc: print(dong) # Mỗi dòng là một list
Excel cài đặt tiếng Việt/khu vực Việt Nam thường dùng dấu chấm phẩy ; làm phân cách CSV, vì dấu phẩy đã dùng làm dấu thập phân.
# Đọc file CSV do Excel VN xuấtwith open("tu_excel.csv", newline="", encoding="utf-8-sig") as f: doc = csv.DictReader(f, delimiter=";") ...# Ghi file CSV để Excel VN mở đúng cộtwith open("cho_excel.csv", "w", newline="", encoding="utf-8-sig") as f: ghi = csv.DictWriter(f, fieldnames=cot, delimiter=";") ...
Checklist khi làm việc với CSV + Excel + tiếng Việt
newline="" — luôn có
encoding="utf-8-sig" — để Excel hiển thị đúng tiếng Việt
delimiter=";" — nếu Excel dùng khu vực Việt Nam
Ép kiểu sau khi đọc — mọi giá trị đều là chuỗi
7.7. Chương trình hoàn chỉnh — Xử lý CSV
"""Đọc file CSV hàng hóa, tính toán và xuất báo cáo."""import csvfrom decimal import Decimalfrom pathlib import PathFILE_VAO = Path("hang_hoa.csv")FILE_RA = Path("bao_cao_ton_kho.csv")def doc_hang_hoa(duong_dan): """Đọc file CSV hàng hóa, trả về (danh sách, lỗi).""" hang = [] loi = [] if not duong_dan.exists(): loi.append(f"Khong tim thay file: {duong_dan}") return hang, loi try: with open(duong_dan, newline="", encoding="utf-8-sig") as f: doc = csv.DictReader(f) cot_bat_buoc = {"ma", "ten", "so_luong", "don_gia"} thieu = cot_bat_buoc - set(doc.fieldnames or []) if thieu: loi.append(f"File thieu cot: {', '.join(sorted(thieu))}") return hang, loi for so_dong, dong in enumerate(doc, start=2): # start=2 vì dòng 1 là header try: hang.append({ "ma": dong["ma"].strip(), "ten": dong["ten"].strip(), "don_vi": dong.get("don_vi", "").strip(), "so_luong": int(dong["so_luong"]), "don_gia": Decimal(dong["don_gia"]), }) except (ValueError, KeyError) as e: loi.append(f"Dong {so_dong}: {e}") except UnicodeDecodeError as e: loi.append(f"Loi bang ma: {e}") except PermissionError: loi.append(f"Khong co quyen doc file: {duong_dan}") return hang, loidef xuat_bao_cao_csv(hang, duong_dan): """Xuất báo cáo tồn kho ra file CSV cho Excel.""" cot = ["stt", "ma", "ten", "don_vi", "so_luong", "don_gia", "thanh_tien"] with open(duong_dan, "w", newline="", encoding="utf-8-sig") as f: ghi = csv.DictWriter(f, fieldnames=cot, delimiter=";") ghi.writeheader() for i, h in enumerate(hang, start=1): ghi.writerow({ "stt": i, "ma": h["ma"], "ten": h["ten"], "don_vi": h["don_vi"], "so_luong": h["so_luong"], "don_gia": h["don_gia"], "thanh_tien": h["so_luong"] * h["don_gia"], }) print(f"Da xuat bao cao: {duong_dan.resolve()}")def main(): hang, loi = doc_hang_hoa(FILE_VAO) if loi: print("CO LOI KHI DOC FILE:") for l in loi: print(f" - {l}") print() if not hang: print("Khong co du lieu de xu ly.") return # In ra màn hình print(f"{'MA':<8}{'TEN HANG':<26}{'SL':>6}{'DON GIA':>15}{'THANH TIEN':>17}") print("-" * 72) tong = Decimal("0") for h in hang: tt = h["so_luong"] * h["don_gia"] tong += tt print(f"{h['ma']:<8}{h['ten']:<26}{h['so_luong']:>6}" f"{h['don_gia']:>15,.0f}{tt:>17,.0f}") print("-" * 72) print(f"{'TONG GIA TRI TON KHO':<55}{tong:>17,.0f}") xuat_bao_cao_csv(hang, FILE_RA)if __name__ == "__main__": main()
import jsonwith open("du_lieu.json", encoding="utf-8") as f: du_lieu = json.load(f)print(du_lieu["cong_ty"])for kh in du_lieu["khach_hang"]: print(f" {kh['ma']}: {kh['ten']} - {kh['cong_no']:,}")
8.3. Phân biệt 4 hàm
Hàm
Chiều
Đối tượng
json.dump(obj, file)
Python → file
File
json.dumps(obj)
Python → chuỗi
Chuỗi
json.load(file)
File → Python
File
json.loads(s)
Chuỗi → Python
Chuỗi
Mẹo nhớ: chữ s cuối = string (chuỗi).
8.4. ⚠️ JSON không lưu được mọi kiểu Python
import jsonfrom datetime import datefrom decimal import Decimaldu_lieu = {"ngay": date.today()}json.dumps(du_lieu) # ❌ TypeError: Object of type date is not JSON serializabledu_lieu = {"tien": Decimal("100.50")}json.dumps(du_lieu) # ❌ TypeError: Object of type Decimal is not JSON serializable
def chuyen_doi(o): if isinstance(o, (date, datetime)): return o.isoformat() if isinstance(o, Decimal): return str(o) raise TypeError(f"Khong the chuyen doi kieu {type(o)}")json.dumps(du_lieu, default=chuyen_doi, ensure_ascii=False)
8.5. Ứng dụng — File cấu hình
import jsonfrom pathlib import PathFILE_CAU_HINH = Path("cau_hinh.json")CAU_HINH_MAC_DINH = { "ten_cong_ty": "Công ty TNHH ABC", "mst": "3300123456", "thue_suat_vat": 0.1, "nguong_chiet_khau": 10000000, "duong_dan_xuat": "./bao_cao",}def doc_cau_hinh(): """Đọc cấu hình, tạo file mặc định nếu chưa có.""" if not FILE_CAU_HINH.exists(): ghi_cau_hinh(CAU_HINH_MAC_DINH) print(f"Da tao file cau hinh mac dinh: {FILE_CAU_HINH}") return CAU_HINH_MAC_DINH.copy() try: with open(FILE_CAU_HINH, encoding="utf-8") as f: ch = json.load(f) except json.JSONDecodeError as e: print(f"File cau hinh loi dinh dang: {e}") print("Dung cau hinh mac dinh.") return CAU_HINH_MAC_DINH.copy() # Bổ sung khóa thiếu for k, v in CAU_HINH_MAC_DINH.items(): ch.setdefault(k, v) return chdef ghi_cau_hinh(ch): """Ghi cấu hình ra file.""" with open(FILE_CAU_HINH, "w", encoding="utf-8") as f: json.dump(ch, f, ensure_ascii=False, indent=2)if __name__ == "__main__": ch = doc_cau_hinh() print(f"Cong ty : {ch['ten_cong_ty']}") print(f"Thue suat : {ch['thue_suat_vat']:.0%}")
9. Kiểm tra và quản lý file với pathlib
from pathlib import Pathp = Path("du_lieu/khach_hang.csv")# Kiểm traprint(p.exists()) # File có tồn tại khôngprint(p.is_file()) # Có phải file khôngprint(p.is_dir()) # Có phải thư mục không# Thông tinprint(p.name) # khach_hang.csvprint(p.stem) # khach_hangprint(p.suffix) # .csvprint(p.parent) # du_lieuprint(p.resolve()) # Đường dẫn tuyệt đốiif p.exists(): print(p.stat().st_size, "bytes") # Kích thước file# Tạo thư mụcPath("bao_cao/2026/thang_08").mkdir(parents=True, exist_ok=True)# Liệt kê filethu_muc = Path("du_lieu")for f in thu_muc.glob("*.csv"): # Chỉ thư mục này print(f)for f in thu_muc.glob("**/*.csv"): # Cả thư mục con print(f)# Đọc/ghi nhanh (file nhỏ)noi_dung = Path("ghi_chu.txt").read_text(encoding="utf-8")Path("ghi_chu.txt").write_text("Noi dung moi", encoding="utf-8")# Xóa, đổi tên# p.unlink() # Xóa file# p.rename("ten_moi.csv") # Đổi tên
10. Thực hành bắt buộc
Bài 1 — Ghi và đọc file text
Tạo file nhat_ky.txt, ghi 5 dòng bất kỳ
Đọc lại và in ra kèm số thứ tự dòng
Thêm 2 dòng nữa (mode "a"), đọc lại kiểm tra
Đếm số dòng, số từ, số ký tự của file
Bài 2 — Phân tích file log
Tạo file he_thong.log với nội dung mẫu:
2026-08-08 08:15:00 INFO Khoi dong he thong
2026-08-08 08:15:03 INFO Ket noi database thanh cong
2026-08-08 09:22:11 WARNING Bo nho su dung 85%
2026-08-08 10:05:47 ERROR Khong the ghi file bao cao
2026-08-08 10:05:48 ERROR Timeout khi goi API
2026-08-08 11:30:00 INFO Hoan tat dong bo du lieu
Viết chương trình:
Đếm số dòng theo từng mức (INFO/WARNING/ERROR)
In ra chỉ các dòng ERROR
Tìm dòng ERROR đầu tiên và cuối cùng
Xuất các dòng ERROR ra file loi.txt
Bài 3 — Đọc CSV
Tạo file nhan_vien.csv với các cột: ma, ho_ten, phong_ban, luong, ngay_vao_lam. Nhập ít nhất 10 dòng dữ liệu (có tiếng Việt có dấu).
Từ dữ liệu bài 3, xuất ra file bao_cao_nhan_su.csv sao cho mở bằng Excel tiếng Việt hiển thị đúng (đúng cột, đúng dấu tiếng Việt). Kiểm chứng bằng cách mở thật bằng Excel.
Bài 5 — JSON
Chuyển dữ liệu nhân viên ở bài 3 sang file JSON
Đọc lại file JSON, in ra
Thêm một nhân viên mới rồi ghi lại
Thử ghi một date vào JSON, xử lý lỗi phát sinh
Bài 6 — Chuyển đổi định dạng
Viết chương trình chuyển đổi qua lại: CSV → JSON và JSON → CSV.
Bài 7 — Xử lý lỗi file
Viết hàm doc_file_an_toan(duong_dan) xử lý đủ:
File không tồn tại
Không có quyền đọc
Lỗi encoding
Đường dẫn là thư mục chứ không phải file
File rỗng
Mỗi trường hợp có thông báo riêng, hàm không bao giờ ném lỗi ra ngoài.
Bài 8 — ÔN TẬP GIAI ĐOẠN 3
Viết chương trình so_thu_chi.py:
Lưu trữ: file JSON giao_dich.json
Chức năng:
Thêm giao dịch (ngày, loại thu/chi, danh mục, số tiền, ghi chú)
Xem danh sách (có lọc theo tháng, theo loại)
Sửa/xóa giao dịch
Báo cáo tổng thu, tổng chi, số dư
Báo cáo theo danh mục
Xuất báo cáo ra CSV cho Excel
Yêu cầu kỹ thuật:
Chia thành ít nhất 3 module
Mọi hàm có docstring
Xử lý lỗi đầy đủ (không bao giờ sập)
Dùng Decimal cho tiền
Dùng datetime cho ngày
Dữ liệu tự động lưu sau mỗi thay đổi
Đây là bài kiểm tra giai đoạn 3.
11. Tự kiểm tra
Tôi luôn dùng with open(...) thay vì open() + close()
Tôi luôn chỉ định encoding="utf-8"
Tôi biết utf-8-sig dùng cho file CSV liên quan Excel
Tôi biết mode "w" xóa sạch file cũ
Tôi biết duyệt file theo dòng để tiết kiệm bộ nhớ
Tôi biết csv.DictReader và csv.DictWriter
Tôi biết newline="" là bắt buộc với module csv
Tôi phân biệt json.dump và json.dumps
Tôi biết ensure_ascii=False cho tiếng Việt
Tôi xử lý được FileNotFoundError, PermissionError, UnicodeDecodeError