DHDataHọc
Lý thuyếtBài 25/304 phút đọc+10 XP

Đọc CSV

Bạn sẽ học được

  • Đọc CSV bằng csv.DictReader mà không cần thư viện ngoài.
  • Vì sao mọi giá trị đọc từ CSV đều là chuỗi.
  • Ghi CSV bằng DictWriter an toàn với dấu phẩy trong dữ liệu.
  • Xử lý encoding, BOM và file lỗi.
  • Khi nào nên chuyển sang pandas.

Khái niệm cốt lõi

CSV là văn bản thuần: mỗi dòng một bản ghi, các trường cách nhau bằng dấu phẩy. Module csv của thư viện chuẩn xử lý đúng các trường hợp khó như dấu phẩy trong nội dung, dấu nháy, xuống dòng — những thứ mà split(",") làm sai.

code
import csv
import io

raw = """ngay,mat_hang,so_luong,don_gia
2026-01-05,Áo thun,2,150000
2026-01-06,Quần jean,1,450000
2026-01-07,Áo thun,3,150000
"""

reader = csv.DictReader(io.StringIO(raw))
rows = list(reader)
print(rows[0])
code
{'ngay': '2026-01-05', 'mat_hang': 'Áo thun', 'so_luong': '2', 'don_gia': '150000'}

DictReader dùng dòng đầu làm tên cột và trả mỗi dòng thành dict. Trong file thật, thay io.StringIO(raw) bằng open("sales.csv", newline="", encoding="utf-8").

Hai tham số quan trọng khi open:

  • newline="" để module csv tự xử lý ký tự xuống dòng, tránh dòng trống trên Windows.
  • encoding="utf-8" để đọc đúng tiếng Việt. File xuất từ Excel thường có BOM — dùng utf-8-sig để bỏ BOM ở đầu.

Mọi giá trị đều là chuỗi

Đây là điểm quan trọng nhất khi mới làm việc với CSV. DictReader không suy luận kiểu; so_luong là '2', không phải 2.

code
for row in rows:
    revenue = int(row["so_luong"]) * int(row["don_gia"])
    print(row["ngay"], row["mat_hang"], revenue)
code
2026-01-05 Áo thun 300000
2026-01-06 Quần jean 450000
2026-01-07 Áo thun 450000

Nếu quên ép kiểu, row["so_luong"] * row["don_gia"] sẽ nhân chuỗi với chuỗi và báo TypeError: can't multiply sequence by non-int of type 'str'. Hoặc tệ hơn, "2" + "3" cho "23" — không lỗi nhưng sai hoàn toàn.

Tổng doanh thu:

code
total = sum(int(r["so_luong"]) * int(r["don_gia"]) for r in rows)
print(total)
code
1200000

Trong dữ liệu thật, cột số có thể chứa dấu phẩy hàng nghìn ("1,200,000"), khoảng trắng, hoặc ô trống. Hàm chuyển đổi cần xử lý:

code
def to_int(raw):
    if not raw or not raw.strip():
        return 0
    return int(raw.replace(",", "").strip())

Với số thực, dùng float và lưu ý dấu thập phân: CSV kiểu Việt có thể dùng , — khi đó phải biết quy ước của nguồn trước khi thay dấu.

Ghi CSV

code
out = io.StringIO()
writer = csv.DictWriter(out, fieldnames=["ngay", "mat_hang", "so_luong", "don_gia"])
writer.writeheader()
writer.writerows(rows)
print(out.getvalue().strip().splitlines()[0])
code
ngay,mat_hang,so_luong,don_gia

Với file thật:

code
with open("clean.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=["ngay", "mat_hang", "so_luong", "don_gia"])
    writer.writeheader()
    writer.writerows(rows)
  • fieldnames phải khớp khóa trong dict, nếu không DictWriter báo ValueError.
  • extrasaction="ignore" bỏ qua khóa thừa nếu dict có cột ngoài danh sách.
  • encoding="utf-8-sig" giúp Excel mở file có tiếng Việt không bị lỗi font.

Không tự nối chuỗi bằng ",".join(...): nếu trường chứa dấu phẩy, dấu nháy hoặc xuống dòng, file sẽ hỏng. csv tự bọc nháy và escape đúng chuẩn.

Đọc file lớn

reader là iterator, không nạp toàn bộ file vào bộ nhớ. Với file lớn, xử lý từng dòng:

code
with open("sales.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        xu_ly(row)

Chỉ dùng list(reader) khi thực sự cần duyệt nhiều lần hoặc file nhỏ. Với hàng triệu dòng, pandas.read_csv nhanh hơn và có kiểu dữ liệu tự động, nhưng csv đủ và không cần cài thêm gì.

Thực hành

  1. Đọc đoạn CSV mẫu bằng DictReader, in dòng đầu.
  2. Ép so_luong và don_gia sang int, tính doanh thu từng dòng.
  3. Tính tổng doanh thu bằng generator.
  4. Ghi lại dữ liệu ra CSV bằng DictWriter, mở bằng Excel để kiểm tra tiếng Việt.
  5. Thử split(",") trên một trường có dấu phẩy để thấy vì sao cần module csv.
  6. Đọc file với utf-8-sig và so sánh khi dùng utf-8.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
TypeError khi nhânQuên ép kiểu chuỗiint()/float() trước khi tính
"23" thay vì 5Cộng chuỗiÉp số trước khi cộng
Dòng trống giữa fileThiếu newline=""Thêm khi open
Tiếng Việt bị lỗi fontSai encoding/BOMutf-8-sig
UnicodeDecodeErrorFile không phải UTF-8Thử latin-1 hoặc xác định nguồn
KeyError cộtTên cột lệch hoa thường/khoảng trắngIn reader.fieldnames
File ghi hỏngTự nối chuỗi bằng dấu phẩyDùng csv.writer
ValueError khi ghiDict thừa khóaextrasaction="ignore"

Tóm tắt

  • csv.DictReader trả dict theo tên cột; DictWriter ghi an toàn.
  • Mọi giá trị CSV là chuỗi — luôn ép kiểu trước khi tính.
  • open(..., newline="", encoding="utf-8"); dùng utf-8-sig cho Excel.
  • Reader là iterator, phù hợp file lớn.
  • Không tự tách/ghép CSV bằng chuỗi thô.

Dùng ← → để chuyển bài