Tổng Quan Về RegExp
Biểu thức chính quy (Regular Expression) hay còn gọi tắt là Regex, được coi là một loại dữ liệu phức tạp trong JavaScript (thuộc kiểu object). Công cụ này đóng vai trò then chốt trong việc xử lý chuỗi ký tự, chủ yếu phục vụ cho hai mục đích cốt lõi:
- Xác thực tính hợp lệ của chuỗi dựa trên các quy tắc định nghĩa.
- Khai thác và trích xuất các đoạn văn bản đáp ứng tiêu chí đã chọn.
Cách Khởi Tạo Đối Tượng RegExp
Trong môi trường JS, người phát triển có thể tạo ra các biểu thức chuẩn theo hai phương thức khác nhau:
1. Sử Dụng Literal Syntax
Đây là cách viết trực tiếp trên code bằng dấu gạch chéo.
const pattern = /^[A-Z]{3}\d{3}$/;
2. Sử Dụng Hàm Constructor
Sử dụng hàm dựng sẵn để tạo đối tượng thông qua chuỗi ký tự.
const regexInstance = new RegExp('^[A-Z]{3}\\d{3}$');
Một số điểm phân biệt giữa hai cách này gồm:
- Cú pháp: Một bên là literal, một bên là constructor.
- Ghép nối chuỗi: Chỉ Constructor mới hỗ trợ động việc ghép nối nội dung chuỗi mẫu dễ dàng hơn.
- Escaping: Khi dùng constructor (chuỗi tham số), các ký tự đặc biệt cần thoát kỹ hơn (ví dụ `\\d` thay vì `\d`).
Hàm Xử Lý Chuỗi Cơ Bản
Có hai phương thức chính thuộc đối tượng RegExp để thao tác với dữ liệu:
Kiểm Tra Sự Phù Hợp (Test)
Dùng để xác nhận chuỗi đầu vào có tuân thủ đúng quy luật hay không. Phương thức này trả về giá trị boolean.
// Kết quả trả về true nếu khớp, ngược lại là false
const isValid = pattern.test('XYZ123');
Trích Xuất Dữ Liệu (Exec)
Lấy về phần tử con trùng khớp với mô tả mẫu.
const result = pattern.exec('XYZ123');
// Nếu không tìm thấy: trả về null
// Nếu tìm thấy: trả về Mảng. Index [0] chứa chuỗi đầy đủ vừa bắt được.
// Lưu ý: Mặc định chỉ lấy kết quả đầu tiên dù chuỗi có nhiều phần tử.
Các Loại Ký Tự Đặc Biệt (Metacharacters)
Metacharacter đại diện cho các mẫu ký tự chung, giúp rút gọn cú pháp viết mã.
Ký Tự Cốt Lõi
\s: Khoảng trắng.\d: Chữ số từ 0-9.\w: Chữ cái, số hoặc dấu gạch dưới (A-Za-z0-9_)..: Bất kỳ ký tự nào trừ ký tự xuống dòng.
Vị Trí Biên Giới
^: Bắt đầu chuỗi.$: Kết thúc chuỗi.
Lượng Từ (Quantifiers)
Sử dụng để định lượng số lần xuất hiện của ký tự phía trước:
*: 0 hoặc nhiều lần.+: 1 hoặc nhiều lần.?: 0 hoặc 1 lần.{n,m}: Từ n đến m lần.
Tính Tham Lam Và Không Tham Lam
Khi áp dụng lượng từ, hành vi mặc định của Regex là "Tham lam" (greedy), tức là cố gắng lấy càng nhiều ký tự càng tốt theo phạm vi cho phép.
Để chuyển sang chế độ "Không tham lam" (non-greedy/lazy), hãy thêm dấu hỏi sau lượng từ. Khi đó, nó sẽ dừng lại ở điểm nhỏ nhất thỏa mãn điều kiện.
// Tham lam: Lấy hết các số liền kề
const greedy = /\d+/g;
greedy.exec('a12345b'); // Kết quả: 12345
// Không tham lam: Lấy số ít nhất có thể
const nonGreedy = /\d+?/g;
nonGreedy.exec('a12345b'); // Kết quả: 1
Cấu Trúc Nhóm Và Lựa Chọn
Nhóm Chuẩn (): Dùng để gom nhóm các ký tự. Các nhóm này cũng được tách riêng ra trong kết quả trả về theo thứ tự mở ngoặc.
const group = /(abc)\s+(def)/;
console.log(group.exec('abc def'));
// Mảng[0]: abc def
// Mảng[1]: abc
// Mảng[2]: def
Nhóm Không Lưu (?:): Gom nhóm nhưng không lưu vào bộ nhớ tạm để truy vết.
const noCapture = /(?:abc){2}/; // Chỉ khớp abcabc, nhưng không giữ nhóm abc riêng
Toán Tử Or (|): Chọn giữa nhiều khả năng.
const choice = /(red|green|blue)/; // Tìm màu đỏ, xanh lá hoặc xanh dương
Khoảng Giá Trị []: Chọn 1 ký tự trong danh sách.
const range = /[a-z]/; // Hoặc [a-zA-Z]
const negative = /[^0-9]/; // Chọn ký tự KHÔNG phải là số
Kiểm Soát Vị Trí (Lookaround)
Công cụ mạnh mẽ để kiểm tra ngữ cảnh xung quanh mà không đưa vào kết quả trả về.
- Positive Lookahead (?=): Yêu cầu chuỗi sau phải khớp điều kiện.
- Negative Lookahead (?!): Yêu cầu chuỗi sau KHÔNG khớp điều kiện.
- Positive Lookbehind (?<=): Kiểm tra nội dung trước đó (Hỗ trợ ES2018+).
- Negative Lookbehind (?<!): Đảm bảo nội dung trước đó KHÔNG khớp.
const afterCheck = /win(?=ner)/;
console.log(afterCheck.test('winner')); // true (chỉ khớp win)
const notAfter = /win(?!ner)/;
console.log(notAfter.test('winsome')); // true
const beforeCheck = /(?<=\$)\d+/;
console.log(beforeCheck.test('$100')); // Tìm số ngay sau dấu $
Tái Sử Dụng Mẫu (Backreference)
Sử dụng số nguyên để tham chiếu lại nhóm capture đã bắt ở trước đó, đảm bảo nội dung lặp lại y hệt.
const repeat = /(bob|dave)-\1/;
console.log(repeat.test('bob-bob')); // true
console.log(repeat.test('bob-dave')); // false
Định Danh (Flags)
Gắn vào cuối biểu thức để thay đổi hành vi toàn cục:
i: Bỏ qua chữ HOA/thường.g: Tìm kiếm tất cả (Global), không dừng ở kết quả đầu tiên.y: Tính dính (Sticky), bám sát vị trí bắt đầu, nếu bỏ lỡ sẽ fail ngay.
Phối Hợp Với Các Phương Thức Chuỗi
Chuỗi String có thể trực tiếp chấp nhận Regex làm tham số cho các hàm tìm kiếm và thay thế.
Tìm Kiếm (search):
const index = 'email@example.com'.search(/@/); // Trả về vị trí ký tự @
Thay Thế (replace):
let fixedStr = 'abc123def'.replace(/\d/g, 'x');
// abcxxxdef
// Thêm cờ 'g' để thay thế tất cả các số, không chỉ số đầu tiên.
Truy Vấn (match):
const matches = 'apple banana pear'.match(/a.+e/g);
// Nếu không có cờ g: trả về mảng cấu trúc giống exec.
// Nếu có cờ g: trả về mảng liệt kê các chuỗi con phù hợp.
Định Dạng Unicode (Ví Dụ Tiếng Nhật/Hàn/Trung)
Để khớp văn bản ngoài bảng ASCII, dùng mã Unicode.
// Khoả chữ Hán cơ bản
const chinese = /[\u4e00-\u9fa5]+/;
console.log('你好 world'.match(chinese)); // ['你好']