
Regex là gì?
Regex (regular expression, hay biểu thức chính quy) là một chuỗi ký tự đặc biệt dùng để định nghĩa một mẫu (pattern) tìm kiếm, xác thực và xử lý văn bản trong chương trình. Thay vì viết từng bước kiểm tra chuỗi, lập trình viên viết một pattern và để engine regex giải quyết, giúp xử lý văn bản nhanh và chính xác hơn hẳn.
Khái niệm này bắt nguồn từ toán học gia Stephen Cole Kleene vào năm 1951 với lý thuyết ngôn ngữ chính quy (regular language), sau đó được phổ biến qua các công cụ Unix như sed, awk, grep và giờ có mặt trong mọi ngôn ngữ lập trình hiện đại. Đây là kỹ năng gần như bắt buộc với bất kỳ lập trình viên nào.
Cú pháp cơ bản
Ký tự đặc biệt
.– khớp bất kỳ ký tự nào (trừ xuống dòng)^– bắt đầu chuỗi,$– kết thúc chuỗi– escape ký tự đặc biệt (.khớp dấu chấm thật)|– toán tử “hoặc” (a|bkhớp a hoặc b)
Lớp ký tự (character classes)
[abc]– một trong các ký tự a, b, c;[^abc]– ngược lại[a-z]– khoảng ký tự từ a đến zd– chữ số[0-9],w– ký tự word[a-zA-Z0-9_],s– khoảng trắngb– ranh giới từ:bcatbkhớp “cat” nhưng không khớp “catalog”
Bộ định lượng (quantifiers)
*– 0 hoặc nhiều lần;+– 1 hoặc nhiều lần;?– 0 hoặc 1 lần{n}– chính xác n lần;{n,}– ít nhất n lần;{n,m}– từ n đến m lần*?,+?– phiên bản lazy (khớp ít nhất có thể)
Nhóm và lookaround
(...)– capturing group, lưu kết quả vào bộ nhớ(?:...)– non-capturing group, chỉ nhóm không lưu(?=...)– lookahead dương,(?!...)– lookahead âm(?<=...)– lookbehind dương,(?<!...)– lookbehind âm
Ví dụ: (?<=d)[a-z]+(?=d) lấy chuỗi chữ cái nằm giữa hai chữ số trong “123abc123” → kết quả “abc”.
Mẫu regex thường dùng
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$
Số điện thoại Việt Nam
^(+84|84|0)[3-9]d{8}$
URL
^https?://[w.-]+(?:.[w.-]+)+(?:/[w._~:/?#[]@!$&'()*+,;=-]*)?$
Mật khẩu mạnh
^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&])[A-Za-zd@$!%*?&]{8,}$
Mẫu trên yêu cầu ít nhất 8 ký tự gồm chữ thường, chữ hoa, số và ký tự đặc biệt. Lưu ý regex không thể kiểm tra email 100% theo chuẩn RFC 5322, nên chỉ dùng cho validation cơ bản và luôn xác minh thêm qua link xác nhận.
Các phiên bản regex phổ biến
| Flavor | Đặc điểm |
|---|---|
| PCRE/PCRE2 | Phổ biến nhất, đầy đủ tính năng; dùng trong PHP, Nginx, Rust; regex101 mặc định PCRE2 |
| JavaScript | Tích hợp sẵn, cú pháp /pattern/flags; có test(), match(), replace() |
| Python re | Standard library; dùng raw string r"...", hàm re.match(), re.findall(), re.sub() |
| POSIX ERE/BRE | Chuẩn UNIX, dùng trong grep, sed, awk; ít tính năng hơn PCRE |
| Go/Rust regexp | Đảm bảo thời gian chạy tuyến tính O(n), không backtracking |
Mẹo tối ưu và tránh ReDoS
Regex có thể gây lỗ hổng ReDoS (Regular Expression Denial of Service) khi pattern backtracking quá nhiều trên input không khớp, ví dụ ^(a+)+$ chạy trên chuỗi “aaaaaaaaaaaaaaaaab”. Cách xử lý: dùng atomic group (?>...) hoặc possessive quantifier a++ (PCRE2), hoặc viết lại pattern đơn giản hơn.
- Dùng
re.compile()trong Python khi tái sử dụng pattern nhiều lần - Ưu tiên non-capturing group
(?:...)khi không cần lưu kết quả - Tránh greedy quantifier không cần thiết, dùng
*?thay* - Luôn test pattern trên nhiều loại input trước khi đưa vào production
Các trường hợp sử dụng chính
Xác thực đầu vào
Regex là công cụ số một để kiểm tra định dạng dữ liệu nhập từ người dùng: email, số điện thoại, mật khẩu độ mạnh, ngày tháng, mã bưu điện hay số căn cước. Thay vì viết hàng chục dòng điều kiện phức tạp, một pattern ngắn gọn thay thế toàn bộ.
Tìm kiếm và thay thế
Tìm ký tự đặc biệt trong văn bản lớn, đổi định dạng ngày từ DD/MM/YYYY sang YYYY-MM-DD, làm sạch dữ liệu bằng cách loại bỏ ký tự không mong muốn. Đây là thao tác phổ biến trong các IDE và công cụ xử lý log.
Phân tách và trích xuất dữ liệu
Trích xuất thông tin từ log file, parse CSV/XML/JSON đơn giản, lọc ra URL, email, số điện thoại từ văn bản thuần. Regex cho phép tokenizer cho ngôn ngữ dịch và lọc dữ liệu đầu vào API nhanh chóng.
Cách dùng trong Python và JavaScript
Trong Python, module re nằm trong standard library với các hàm chính re.match(), re.search(), re.findall(), re.sub(), re.split(). Quy tắc quan trọng là luôn dùng raw string r"pattern" để tránh xung đột với backslash, và dùng re.compile() khi tái sử dụng pattern nhiều lần để tối ưu hiệu suất.
Trong JavaScript, regex được viết với cú pháp /pattern/flags (ví dụ /hello/i – không phân biệt hoa thường) và có các method test(), match(), replace(), split(). Từ ES2018 trở đi, JavaScript hỗ trợ thêm named groups (?<name>...), lookbehind và unicode property p{...}.
Các flag (cờ hiệu) thường dùng: i – không phân biệt hoa thường, g – match toàn bộ trong JavaScript, m – multi-line để ^ và $ khớp mỗi dòng, s – dotall cho phép . khớp cả xuống dòng, x – verbose mode cho phép viết comment trong pattern (Python).
Bộ định lượng và nhóm nâng cao
Ngoài nhóm thông thường, regexp hỗ trợ named capturing group (?<name>...) giúp đọc kết quả rõ ràng hơn khi có nhiều nhóm, và backreference 1 để tham chiếu lại nhóm đã bắt – hữu ích khi cần tìm cặp ký tự lặp lại như tag HTML mở/đóng. Alternation | cho phép khớp nhiều lựa chọn trong một pattern, ví dụ (cat|dog) khớp “cat” hoặc “dog”.
Ví dụ tìm email xuất hiện trong đoạn văn bản dài, regexp [w.+-]+@[w-]+.[w.]+ sẽ quét và trích xuất toàn bộ địa chỉ email. Ví dụ tìm số điện thoại Việt Nam linh hoạt: (+84|84|0)[3-9]d{8} – xử lý cả mã quốc gia +84 lẫn số bắt đầu bằng 0 thường gặp.
Công cụ hỗ trợ luyện tập
Công cụ tốt nhất hiện nay là regex101.com – hỗ trợ PCRE2, JavaScript, Python, Go, Java, .NET, Rust, POSIX với debug realtime, giải thích pattern tự động và thư viện mẫu cộng đồng. Ngoài ra có regexper.com vẽ sơ đồ đường ray (railroad diagram) trực quan, và debuggex.com cho JavaScript, Python, PCRE. Trong IDE, VS Code hỗ trợ regex ở tìm/thay thế (Ctrl+F), JetBrains có regex tester tích hợp.
Tổng kết: hãy bắt đầu từ cú pháp cơ bản (., *, +, [], d, w), thực hành trên regex101 và luôn nhớ kiểm tra hiệu suất để tránh ReDoS. Tham khảo thêm tài liệu chính thức của Python re module, hướng dẫn regex JavaScript của MDN và bài tổng quan trên Wikipedia.
