Trang chủ › Bài viết › Tại sao Regex không khớp
Lập trình viên & Mã nguồn

Tại sao Regex không khớp? Tìm hiểu cờ Flags, chế độ Multiline và ký tự Escape

Ngày xuất bản: 16 tháng 10, 2026 • Cập nhật: 17 tháng 10, 2026 • Tác giả: Đội ngũ Kỹ thuật GoToolstack • Xác thực: Công cụ Kiểm tra Regex GoToolstack
Kiểm tra và trực quan hóa biểu thức chính quy: Hỗ trợ đầy đủ các cờ g, i, m, s, u, tô màu nhóm bắt (capture groups), giải thích cú pháp chi tiết và phát hiện lỗi theo thời gian thực.
Mở công cụ Kiểm tra Regex
Mục lục nội dung
  1. Bản chất của các cờ Flags trong biểu thức chính quy
  2. Bẫy trạng thái lastIndex khi dùng cờ g trong vòng lặp
  3. Chế độ Tham lam (Greedy) vs Lười biếng (Lazy)

1. Bản chất của các cờ Flags trong biểu thức chính quy

Biểu thức chính quy (Regular Expressions – RegEx) là công cụ cực kỳ mạnh mẽ để tìm kiếm và trích xuất chuỗi. Tuy nhiên, ngay cả một biểu thức được viết rất cẩn thận cũng có thể trả về kết quả rỗng nếu lập trình viên không thiết lập đúng các cờ điều khiển (Flags):

Cờ Flag Tên đầy đủ Tác động đến hành vi khớp mẫu
g Global Search Tìm kiếm tất cả các kết quả phù hợp trên toàn chuỗi thay vì dừng lại ngay sau kết quả đầu tiên. Duy trì thuộc tính trạng thái lastIndex.
m Multiline Mode Thay đổi hành vi của các ký tự neo ^ và $: khớp ở đầu và cuối của từng dòng riêng biệt thay vì chỉ khớp ở đầu và cuối của toàn bộ văn bản.
s dotAll Mode Cho phép ký tự chấm (.) khớp với cả ký tự ngắt dòng (\n và \r).
i Ignore Case So khớp không phân biệt chữ hoa và chữ thường (ví dụ: /a/i khớp cả 'a' và 'A').
u Unicode Support Xử lý đúng các ký tự Unicode 4-byte (surrogate pairs) và thuộc tính ký tự Unicode \p{...}.

2. Bẫy trạng thái lastIndex khi dùng cờ g trong vòng lặp

Một lỗi phổ biến khiến nhiều lập trình viên JavaScript bối rối là việc hàm RegExp.test() luân phiên trả về true rồi lại false khi kiểm tra các chuỗi giống hệt nhau:

Cạm bẫy với RegExp stateful
const regex = /abc/g;
console.log(regex.test('abc')); // true (lastIndex chuyển thành 3)
console.log(regex.test('abc')); // false (tìm từ vị trí 3, thất bại, lastIndex reset về 0)
console.log(regex.test('abc')); // true (lại thành true!)

Khi bật cờ g, đối tượng RegExp lưu giữ trạng thái vị trí tìm kiếm tiếp theo trong thuộc tính lastIndex. Để kiểm tra tính hợp lệ đơn thuần, bạn nên bỏ cờ g hoặc gán lại regex.lastIndex = 0; trước mỗi lần kiểm tra.

3. Chế độ Tham lam (Greedy) vs Lười biếng (Lazy)

Mặc định, các toán tử lặp lượng từ (*, +, {n,m}) hoạt động ở chế độ tham lam (greedy) – tức là cố gắng khớp càng nhiều ký tự càng tốt. Điều này thường dẫn đến việc bắt nhầm toàn bộ khối văn bản lớn thay vì từng thẻ riêng lẻ:

Ví dụ Greedy vs Lazy
// Chuỗi nguồn: "<b>Một</b> và <b>Hai</b>"

// ❌ Greedy: <.*> khớp toàn bộ từ <b> đầu tiên đến </b> cuối cùng
// Kết quả: "<b>Một</b> và <b>Hai</b>"

// ✅ Lazy: <.*?> dừng lại ngay khi gặp dấu > đầu tiên
// Kết quả: 4 kết quả riêng biệt: "<b>", "</b>", "<b>", "</b>"