# Cách 1 — Raw string (khuyến nghị): thêm r phía trướcduong_dan = r"C:\Users\new\test.txt"# Cách 2 — Nhân đôi dấu gạch chéoduong_dan = "C:\\Users\\new\\test.txt"# Cách 3 — Dùng gạch chéo xuôi (Python hiểu được trên Windows)duong_dan = "C:/Users/new/test.txt"
Raw string (r"...") nghĩa là “chuỗi thô” — Python không xử lý escape sequence bên trong. Rất quan trọng khi làm việc với đường dẫn file ở 13 - Làm việc với File.
2. Indexing — Truy cập từng ký tự
2.1. Chỉ số bắt đầu từ 0
s = "PYTHON"
Ký tự
P
Y
T
H
O
N
Chỉ số dương
0
1
2
3
4
5
Chỉ số âm
-6
-5
-4
-3
-2
-1
s = "PYTHON"print(s[0]) # P — ký tự ĐẦU TIÊNprint(s[1]) # Yprint(s[5]) # N — ký tự cuốiprint(s[-1]) # N — ký tự cuối (cách gọn hơn)print(s[-2]) # Oprint(s[-6]) # P
Bẫy lớn cho dân SQL
Trong T-SQL, hàm SUBSTRING(str, 1, 1) lấy ký tự đầu tiên — chỉ số bắt đầu từ 1. Trong Python, chỉ số bắt đầu từ 0. Đây là nguồn lỗi “off-by-one” (lệch một đơn vị) kinh điển.
SQL
Python
Ký tự đầu
SUBSTRING(s,1,1)
s[0]
Ký tự thứ 3
SUBSTRING(s,3,1)
s[2]
Ký tự cuối
RIGHT(s,1)
s[-1]
2.2. Lỗi vượt giới hạn
s = "PYTHON"print(s[6]) # ❌ IndexError: string index out of rangeprint(s[100]) # ❌ IndexError
Chuỗi có 6 ký tự thì chỉ số hợp lệ là 0–5 (hoặc -1 đến -6).
2.3. Độ dài chuỗi — len()
s = "PYTHON"print(len(s)) # 6print(s[len(s) - 1]) # N — ký tự cuối (cách dài dòng)print(s[-1]) # N — cách Python (khuyến nghị)
3. Slicing — Cắt lát chuỗi
Đây là kỹ thuật quan trọng bậc nhất trong Python. Nó áp dụng cho chuỗi, list, tuple — tức là bạn học một lần dùng suốt.
3.1. Cú pháp
s[start:stop:step]
Tham số
Ý nghĩa
Mặc định
start
Chỉ số bắt đầu — bao gồm
0
stop
Chỉ số kết thúc — KHÔNG bao gồm
hết chuỗi
step
Bước nhảy
1
Ghi nhớ: stop KHÔNG được lấy
s[0:3] lấy các ký tự ở chỉ số 0, 1, 2 — không lấy chỉ số 3.
Cách nhớ: số phần tử lấy được = stop - start.
3.2. Ví dụ cơ bản
s = "PYTHON"print(s[0:3]) # PYT — chỉ số 0,1,2print(s[2:5]) # THO — chỉ số 2,3,4print(s[:3]) # PYT — từ đầu tới chỉ số 2print(s[3:]) # HON — từ chỉ số 3 tới hếtprint(s[:]) # PYTHON — toàn bộ (tạo bản sao)print(s[-3:]) # HON — 3 ký tự cuốiprint(s[:-3]) # PYT — bỏ 3 ký tự cuốiprint(s[1:-1]) # YTHO — bỏ ký tự đầu và cuối
3.3. Bước nhảy step
s = "PYTHON"print(s[::2]) # PTO — lấy cách 1 ký tựprint(s[1::2]) # YHN — từ chỉ số 1, cách 1print(s[::-1]) # NOHTYP — ĐẢO NGƯỢC chuỗiprint(s[::-2]) # NHY
Mẹo s[::-1] để đảo ngược chuỗi là cách viết được dùng rất phổ biến trong Python.
3.4. Slicing không bao giờ báo lỗi vượt giới hạn
s = "PYTHON"print(s[0:100]) # PYTHON — không lỗi, tự cắt tới hếtprint(s[100:200]) # "" — chuỗi rỗng, không lỗiprint(s[100]) # ❌ IndexError — indexing thì CÓ lỗi
Khác biệt này rất hữu ích: slicing an toàn, indexing thì phải cẩn thận.
5.4. Format specifier — Định dạng số (RẤT HỮU ÍCH)
Cú pháp: {gia_tri:dinh_dang}
Số thập phân:
pi = 3.14159265print(f"{pi:.2f}") # 3.14 — 2 chữ số thập phânprint(f"{pi:.4f}") # 3.1416 — 4 chữ số thập phânprint(f"{pi:.0f}") # 3 — không có thập phân
Dấu phân cách hàng nghìn:
tien = 1234567890print(f"{tien:,}") # 1,234,567,890 — dấu phẩy (chuẩn Anh-Mỹ)print(f"{tien:_}") # 1_234_567_890 — dấu gạch dưới
MA HANG TEN HANG SL DON GIA THANH TIEN
---------------------------------------------------------------------------
VT001 Ban lam viec 3 2,500,000 7,500,000
VT002 Ghe xoay 10 850,000 8,500,000
VT003 Tu ho so 2 4,200,000 8,400,000
---------------------------------------------------------------------------
TONG CONG 24,400,000
Tính năng này (từ Python 3.8) rất tiện khi cần kiểm tra giá trị biến lúc gỡ lỗi.
5.7. Lưu ý về dấu nháy
d = {"ten": "An"}print(f"Ten: {d['ten']}") # ✅ dùng nháy đơn bên trong nháy képprint(f'Ten: {d["ten"]}') # ✅ ngược lại cũng được
Muốn in dấu ngoặc nhọn thật thì nhân đôi:
print(f"{{day la ngoac nhon}}") # {day la ngoac nhon}
PHẦN 2 — TUẦN 8
6. String methods — Các phương thức chuỗi
Method (phương thức) là hàm gắn liền với một đối tượng, gọi bằng dấu chấm: chuoi.method().
6.1. Biến đổi chữ hoa/thường
s = "nguyen van a"print(s.upper()) # NGUYEN VAN Aprint(s.lower()) # nguyen van aprint(s.title()) # Nguyen Van A — viết hoa đầu mỗi từprint(s.capitalize()) # Nguyen van a — viết hoa chữ cái đầu tiênprint(s.swapcase()) # NGUYEN VAN A — đảo hoa/thường
SQL
Python
UPPER(s)
s.upper()
LOWER(s)
s.lower()
6.2. Cắt khoảng trắng
s = " Nguyen Van A "print(f"|{s.strip()}|") # |Nguyen Van A| — cắt 2 đầuprint(f"|{s.lstrip()}|") # |Nguyen Van A | — cắt bên tráiprint(f"|{s.rstrip()}|") # | Nguyen Van A| — cắt bên phải
Người dùng rất hay gõ thừa dấu cách. Đây là thói quen bắt buộc khi xử lý dữ liệu.
6.3. Tìm kiếm và kiểm tra
s = "Bao cao tai chinh nam 2026"# in — kiểm tra có chứa khôngprint("tai chinh" in s) # True# find — trả về vị trí, KHÔNG tìm thấy trả về -1print(s.find("tai chinh")) # 8print(s.find("xyz")) # -1# index — giống find nhưng KHÔNG tìm thấy thì báo lỗiprint(s.index("tai chinh")) # 8# print(s.index("xyz")) # ❌ ValueError# count — đếm số lần xuất hiệnprint(s.count("a")) # 6# startswith / endswithprint(s.startswith("Bao")) # Trueprint(s.endswith("2026")) # Trueprint(s.endswith(".xlsx")) # False
SQL
Python
CHARINDEX('x', s) (từ 1, không thấy = 0)
s.find("x") (từ 0, không thấy = -1)
s LIKE 'ABC%'
s.startswith("ABC")
s LIKE '%ABC'
s.endswith("ABC")
s LIKE '%ABC%'
"ABC" in s
6.4. Thay thế
s = "Bao cao thang 07/2026"print(s.replace("07", "08")) # Bao cao thang 08/2026# Thay tất cảprint("a-b-c-d".replace("-", "/")) # a/b/c/d# Chỉ thay N lần đầuprint("a-b-c-d".replace("-", "/", 2)) # a/b/c-d
6.5. Tách và ghép
# split — tách chuỗi thành lists = "KH001,KH002,KH003"print(s.split(",")) # ['KH001', 'KH002', 'KH003']s2 = "Nguyen Van A"print(s2.split()) # ['Nguyen', 'Van', 'A'] — tách theo khoảng trắng, gộp nhiều dấu cách# Giới hạn số lần táchprint("a-b-c-d".split("-", 1)) # ['a', 'b-c-d']# splitlines — tách theo dòngvan_ban = "Dong 1\nDong 2\nDong 3"print(van_ban.splitlines()) # ['Dong 1', 'Dong 2', 'Dong 3']# join — ghép list thành chuỗids = ["KH001", "KH002", "KH003"]print(",".join(ds)) # KH001,KH002,KH003print(" | ".join(ds)) # KH001 | KH002 | KH003print("\n".join(ds)) # mỗi phần tử một dòng
join() gọi từ ký tự phân cách
Cú pháp ",".join(ds) ban đầu trông kỳ lạ (tại sao không phải ds.join(",")?). Lý do: join là method của chuỗi, và ds có thể là list, tuple, set… Nhớ theo mẫu: “dấu phân cách”.join(danh sách).
print("7".zfill(5)) # 00007 — đệm số 0 bên tráiprint("abc".ljust(10, ".")) # abc.......print("abc".rjust(10, ".")) # .......abcprint("abc".center(11, "-")) # ----abc----
7. Unicode và tiếng Việt
7.1. Python 3 hỗ trợ Unicode mặc định
s = "Tiếng Việt có dấu"print(s) # In ra bình thườngprint(len(s)) # 17 — đếm đúng số ký tựprint(s.upper()) # TIẾNG VIỆT CÓ DẤU
Đây là ưu điểm lớn của Python 3 so với Python 2.
7.2. Cẩn thận với len() và ký tự tổ hợp
a = "ế" # Ký tự dựng sẵn (precomposed)b = "ế" # Ký tự tổ hợp (decomposed: e + dấu mũ + dấu sắc)print(len(a), len(b)) # có thể ra 1 và 3print(a == b) # có thể ra False!
Vấn đề này xảy ra khi dữ liệu đến từ macOS hoặc từ các hệ thống khác nhau. Cách chuẩn hóa:
Khi đối chiếu dữ liệu tiếng Việt giữa hệ thống ERP và file Excel/CSV do người dùng gửi, lỗi “hai chuỗi trông giống hệt nhau nhưng so sánh ra False” thường do nguyên nhân này. Luôn normalize("NFC") trước khi so sánh.
7.3. Bảng mã và encoding
s = "Việt Nam"b = s.encode("utf-8") # Chuỗi -> bytesprint(b) # b'Vi\xe1\xbb\x87t Nam'print(b.decode("utf-8")) # bytes -> chuỗi: Việt Nam
Khi đọc file ở 13 - Làm việc với File, luôn chỉ định encoding="utf-8", nếu không sẽ gặp lỗi UnicodeDecodeError với dữ liệu tiếng Việt.
8. Chương trình mẫu tổng hợp
"""In phiếu thu tiền — tổng hợp kiến thức M02 đến M05."""from decimal import Decimal# ---------- Hằng số ----------DO_RONG = 60TEN_CTY = "CONG TY TNHH ABC"# ---------- Nhập liệu ----------print("=" * DO_RONG)print("NHAP THONG TIN PHIEU THU".center(DO_RONG))print("=" * DO_RONG)so_phieu = input("So phieu (chi nhap so): ").strip()ngay = input("Ngay lap (dd/mm/yyyy): ").strip()ten_kh = input("Ho ten nguoi nop: ").strip().title()ly_do = input("Ly do nop: ").strip()so_tien = Decimal(input("So tien (VND): ").strip())# ---------- Xử lý ----------so_phieu_day_du = f"PT{so_phieu.zfill(6)}"so_tien_dinh_dang = f"{so_tien:,.0f}".replace(",", ".")# ---------- Xuất phiếu ----------print()print("=" * DO_RONG)print(TEN_CTY.center(DO_RONG))print("PHIEU THU".center(DO_RONG))print(f"So: {so_phieu_day_du}".center(DO_RONG))print("=" * DO_RONG)print(f"{'Ngay lap':<20}: {ngay}")print(f"{'Ho ten nguoi nop':<20}: {ten_kh}")print(f"{'Ly do nop':<20}: {ly_do}")print(f"{'So tien':<20}: {so_tien_dinh_dang} VND")print("-" * DO_RONG)print(f"{'Nguoi nop tien':^28}{'Thu quy':^28}")print(f"{'(Ky, ho ten)':^28}{'(Ky, ho ten)':^28}")print("=" * DO_RONG)
9. Thực hành bắt buộc
Bài 1 — Slicing
Cho s = "CONG TY TNHH ABC VIET NAM", dùng slicing để in ra:
7 ký tự đầu
8 ký tự cuối
Toàn bộ chuỗi đảo ngược
Các ký tự ở vị trí chẵn
Chuỗi bỏ 4 ký tự đầu và 4 ký tự cuối
Bài 2 — Phân tích mã chứng từ
Cho mã chứng từ dạng PT + YYYYMMDD + NNNN (ví dụ PT202608080015). Viết chương trình nhập mã và in ra:
Nhap ma chung tu: PT202608080015
Loai chung tu : PT
Ngay lap : 08/08/2026
So thu tu : 0015
Bài 3 — Định dạng tiền Việt
Nhập một số tiền, in ra 5 định dạng khác nhau:
Nhap so tien: 1234567.891
1. Nguyen ban : 1234567.891
2. Lam tron : 1234568
3. Dau phay Anh-My : 1,234,567.89
4. Dau cham Viet Nam: 1.234.567,89
5. Can phai 25 ky tu: 1.234.568 d
Bài 4 — Chuẩn hóa họ tên
Nhập họ tên (người dùng có thể gõ lộn xộn hoa thường và thừa dấu cách), in ra dạng chuẩn.
Nhap ho ten: nGuYen vAn a
Ho ten chuan: Nguyen Van A
Nhập số căn cước công dân (12 số), in ra dạng chỉ hiện 3 số cuối:
Nhap so CCCD: 046201001234
Ket qua: *********234
Bài 6 — Kiểm tra email đơn giản
Nhập một email, kiểm tra và in True/False:
Có chứa đúng một ký tự @
Có dấu chấm sau ký tự @
Không bắt đầu hoặc kết thúc bằng @ hoặc .
Bài 7 — Bảng báo cáo
Tự tạo dữ liệu và in bảng báo cáo doanh thu 5 dòng, có tiêu đề, đường kẻ, các cột căn lề đúng, số tiền có dấu phân cách. Dùng f-string với format specifier.
Bài 8 — ÔN TẬP GIAI ĐOẠN 1
Viết chương trình hoa_don_hoan_chinh.py gộp mọi kiến thức từ M02 đến M05:
Nhập thông tin khách hàng (họ tên, mã KH, SĐT)
Nhập 1 mặt hàng (tên, số lượng, đơn giá)
Tính thành tiền, chiết khấu (nếu SL ≥ 10 thì 5%), VAT 10%, tổng cộng
Dùng Decimal cho tiền
In hóa đơn định dạng đẹp bằng f-string
Có comment giải thích từng phần
Đặt tên biến đúng chuẩn PEP 8
Đây là bài kiểm tra giai đoạn 1. Nếu làm được, bạn đã sẵn sàng sang giai đoạn 2.
10. Tự kiểm tra
Tôi hiểu chỉ số Python bắt đầu từ 0 (khác SQL bắt đầu từ 1)
Tôi dùng thành thạo slicing s[start:stop:step] và biết stop không được lấy
Tôi biết s[::-1] để đảo ngược
Tôi hiểu chuỗi là immutable và method chuỗi phải gán lại
Tôi dùng f-string thành thạo, kể cả format specifier :,.2f
Tôi biết split() và join()
Tôi biết strip() dữ liệu nhập vào
Tôi biết dùng raw string r"..." cho đường dẫn Windows