Lập trình 22/09/2026 5 phút

Tham lam vs lười trong regex: vì sao .* nuốt cả chuỗi còn .*? thì không

Cùng một mẫu, chỉ thêm một dấu ?, mà kết quả khác hẳn — và tốc độ cũng vậy. Bài này đo thật bằng python re: greedy .* nuốt cả '<b>xin</b> và <i>bạn</i>' thành một match, lazy .*? tách đúng bốn thẻ; và trên input 200k ký tự, greedy chậm hơn lazy 785 lần vì phải nhả dần (backtrack). Hiểu cơ chế để chọn đúng và tránh lỗi trích sai.

Lập trình 22/09/2026 6 phút

Backreference: viên gạch khiến regex "không còn regular" và buộc phải backtracking

Chỉ một ký hiệu \1 mà đổi cả bản chất lý thuyết của regex. Bài này đo thật: python dùng (\w+)\s+\1 bắt đúng từ lặp 'the the', <(\w+)>...</\1> khớp thẻ đóng đúng thẻ mở; nhưng Go RE2 từ chối biên dịch \1 với lỗi 'invalid escape sequence'. Vì sao khả năng 'nhớ cái đã khớp' vượt khỏi máy trạng thái hữu hạn, buộc engine phải backtracking — và vì sao RE2 cố tình không hỗ trợ.

Lập trình 22/09/2026 5 phút

ReDoS: vì sao một regex 6 ký tự có thể treo cả server của bạn

Mẫu (a+)+$ trông vô hại, nhưng gặp đúng input độc nó chạy chậm theo cấp số nhân. Bài này đo thật: với chuỗi 'aaaa...!', python re mất 9 giây chỉ với 28 ký tự, và thời gian gấp đôi mỗi khi thêm một 'a' — n=40 sẽ mất hàng ngàn năm. Cùng mẫu đó trên Go RE2 chỉ mất 3 micro-giây, kể cả với 100.000 ký tự. Đây là ReDoS, và cách phòng nó.

Lập trình 22/09/2026 6 phút

Tối ưu regex: bốn mẹo đo được, và một mẹo biến 2 giây thành 0.33 micro-giây

Regex chậm hiếm khi vì máy yếu — thường vì mẫu viết chưa khéo. Bài này đo thật bốn kỹ thuật: biên dịch một lần (nhanh 1.84x), lớp phủ định thay lazy (chênh nhỏ nhưng an toàn hơn), atomic/possessive chặn backtracking (2222ms xuống 0.33µs), và neo để loại sớm (68ms xuống 0.21µs, nhanh hơn 329 nghìn lần). Kèm nguyên tắc quan trọng nhất: đo trước khi tối ưu.