Regex Nâng Cao Trong Notepad++: Bộ Training Pack Với Lookahead, Recursion & Parsing Phức Tạp

Bộ Regex Hard Mode Training Pack này tập hợp nhiều pattern từ nâng cao đến khó vô hạn, bao gồm: Lookahead & Lookbehind nhiều tầng để lọc dữ liệu chính xác tuyệt đối; Conditional & Atomic Grouping giúp tối ưu tốc độ match trên file log khổng lồ; Recursion ((?R)) để parse HTML, JSON, và biểu thức toán học lồng nhau; Parsing phức tạp với nhiều điều kiện loại trừ và match chính xác từng block code.

Bộ pack gồm: File dữ liệu test với log, HTML, JSON, code, và text bẩn để luyện tập và hướng dẫn chi tiết cho từng regex, giải thích logic từ cơ bản đến nâng cao.

A. Cơ bản - tìm/lọc ký tự, từ, số, email,...
1. Tìm từ đứng sau foo
Biểu thức: (?<=\bfoo)\w+
Mô tả: Lấy từ ngay sau foo.
Ví dụ: fooBar → match Bar.
2. Tìm từ không đứng sau bar
Biểu thức: (?<!bar\s)(?<!bar)\b\w+\b
Mô tả: Tìm tất cả các từ (\w+) mà ngay trước nó không phải "bar" hoặc "bar ".
Ví dụ: bar apple bar   banana orange barpear grape → match orange và grape
3. Từ đứng trước số
Biểu thức: \w+(?=\d)
Mô tả: Tìm từ mà sau nó là chữ số.
Ví dụ: code123 → match code.
4. Lấy chuỗi giữa [START] và [END]
Biểu thức: (?<=\[START\])(.*?)(?=\[END\])
Mô tả: ?<=\[START\]) lookbehind; (?=\[END\]) lookahead.
Ví dụ: [START]Hello[END] → Hello.
5. Bắt email
Biểu thức: (?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(?:\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\w+)\]))
Phân tích:
Local part (trước dấu @):
 (?:  
  [a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+            # Một hoặc nhiều ký tự hợp lệ (chữ, số, ký tự đặc biệt)  
  (?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*  # Có thể có các phần được nối bằng dấu chấm '.' (ví dụ: abc.def.ghi)  
|  
  "(?:                                                               # Hoặc chuỗi trong dấu ngoặc kép ""  
    [\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]  # Các ký tự ASCII cho phép trong chuỗi "..."  
    |\\[\x01-\x09\x0b\x0c\x0e-\x7f]                       # Hoặc escape ký tự với dấu \  
  )*"  
)
+ @: Đây là dấu ngăn cách bắt buộc giữa local part và domain.
+ Domain part (sau dấu @):
(?:  
  (?:[a-zA-Z0-9]                                # Bắt đầu bằng ký tự chữ hoặc số  
    (?:[a-zA-Z0-9-]*[a-zA-Z0-9])?    # Có thể có các ký tự chữ, số hoặc dấu '-' ở giữa, không để '-' đầu hoặc cuối  
  \.)+                                                  # Các phần này cách nhau dấu chấm '.' (ví dụ: example.com hoặc sub.example.com)  
  [a-zA-Z]{2,}                                  # Phần mở rộng tên miền (TLD) có ít nhất 2 ký tự chữ  
|  
  (?:                                                   # Hoặc domain là một địa chỉ IP nằm trong ngoặc vuông []  
    \[  
      (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}   # 3 nhóm số từ 0-255, mỗi nhóm kết thúc bằng dấu chấm  
      (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\w+)        # Nhóm số cuối (hoặc ký tự chữ số)  
    \]  
  )  
)
Tóm lại:
+ Local part: Chấp nhận đa dạng ký tự, có thể gồm dấu chấm ngăn cách hoặc chuỗi trong dấu "...".
+ Dấu @: bắt buộc.
+ Domain: là chuỗi gồm các phần cách nhau dấu chấm, mỗi phần là ký tự chữ/số với dấu gạch ngang, hoặc địa chỉ IP trong dấu [].
Ví dụ hợp lệ: 
simple@example.com
first.last@example.co.uk
"very.unusual.@.unusual.com"@example.com
user@[192.168.2.1]
B. Trung cấp - lookahead/lookbehind, multiline, non-greedy, nhóm, backreference,...
1. Tìm chữ in hoa liên tục > 3 kí tự
Biểu thức: \b[A-Z]{4,}\b
Mô tả: Phát hiện từ toàn in hoa dài ≥ 4 (ví dụ thống báo lỗi, constants).
2. Match từ lặp 2 lần liên tiếp
Biểu thức: \b(\w+)\s+\1\b
Mô tả: Capture 1 từ vào group 1, rồi dùng backreference \1 để tìm lặp.
Ví dụ: the the → match
C. Nâng cao - atomic grouping, conditional, Unicode properties, negative / dynamic lookarounds,...
1. Conditional pattern
Trong Notepad++ (hoặc bất kỳ engine regex nào hỗ trợ PCRE như trong Python, PHP, Perl), conditional pattern cho phép viết regex với cấu trúc rẽ nhánh, gần giống như câu lệnh if ... else trong lập trình. Cú pháp chung: (?(<condition>)<true-pattern>|<false-pattern>) hoặc (?(condition)yes-pattern|no-pattern)
VD:  Tìm email trong một văn bản, nhưng nếu domain là example.com → bắt buộc phải có số ở đầu username. Ngược lại (các domain khác) → username chỉ được chứa chữ cái và dấu gạch dưới _ (không số).
Biểu thức: \b(?:(?=(?:(\w+)@example\.com))(?=(?(1)\d\w*@example\.com)))|
(?:(?=(?:(\w+)@\w+\.\w+))(?=(?(1)[A-Za-z_]+@\w+\.\w+))))\b
Phân tích:
\b: Bắt đầu tại ranh giới từ để tránh match giữa chữ.
+ Nhánh 1 (domain = example.com): 
   + (?=(?:(\w+)@example\.com)) → Lookahead, bắt nhóm 1 = username, kiểm tra domain là example.com.
   + (?=(?(1)\d\w*@example\.com))) → Conditional: nếu nhóm 1 tồn tại, username phải bắt đầu bằng số.
+ Nhánh 2 (domain khác):
   + (?=(?:(\w+)@\w+\.\w+)) → Lookahead, bắt nhóm 1 = username, domain bất kỳ.
   + (?=(?(1)[A-Za-z_]+@\w+\.\w+))) → Conditional: nếu nhóm 1 tồn tại, username chỉ gồm chữ và _.
Kết hợp bằng | để xử lý cả hai trường hợp.
Ví dụ:
123john@example.com
john123@example.com
abc_def@mydomain.org
abc123@mydomain.org
hello@example.com
9start@example.com
=> Match 123john@example.com, abc_def@mydomain.org, 9start@example.com
2. Phát hiện ký tự ngoài ASCII
Biểu thức: [^\x00-\x7F]
Phân tích: 
+ \x00 → mã hex của ký tự ASCII đầu tiên (NULL)
\x7F → mã hex của ký tự ASCII cuối cùng (DEL).
[^\x00-\x7F] → tìm mọi ký tự không thuộc phạm vi từ \x00 đến \x7F, tức là ký tự ngoài ASCII (Unicode, ký tự tiếng Việt, emoji, ký tự đặc biệt, ký tự tiếng Nhật, tiếng Trung, v.v.).
Mô tả: Tìm mọi ký tự non-ASCII — hữu ích làm sạch file log/code unexpected unicode.
D. Xử lý log / security / data-cleaning
1. Lọc IP ngoài dải private
Biểu thức: \b(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\b
Mô tả: negative lookahead để loại bỏ private ranges (10., 192.168., 172.16-31.*).
2. Phát hiện status code 4xx/5xx trong log
Biểu thức: \s(4\d{2}|5\d{2})\s
Mô tả: Tìm 404, 500,... thường dùng khi log có format [IP] METHOD PATH 404.
E. Recursion, subroutine, parsing lồng nhau
1. Bắt ngoặc lồng (biểu thức toán học)
Biểu thức: \((?:[^()]+|(?R))*\)
Phân tích:
+ \( - bắt dấu mở ngoặc tròn.
(?: ... )* - một nhóm không capture, lặp 0 hoặc nhiều lần, gồm:
    + [^()]+ - bất kỳ ký tự nào ngoại trừ dấu ngoặc tròn, ít nhất 1 ký tự (để không match rỗng quá nhiều lần).
    +  | - hoặc
    + (?R) - gọi lại toàn bộ pattern regex đệ quy (recursive call). Đây chính là phần quan trọng để xử lý các cấp ngoặc bên trong.
\) - dấu đóng ngoặc tròn tương ứng.
Mô tả: Khi regex gặp dấu mở ngoặc (, nó bắt đầu match, sau đó nội dung bên trong có thể là: Các ký tự không phải ngoặc, hoặc một đoạn regex lặp lại chính nó (đệ quy) để bắt các cấp ngoặc tiếp theo. Cuối cùng là dấu đóng ngoặc ) tương ứng với dấu mở.
Ví dụ:
(a + b)
((x + y) * (z - w))
((a + (b * c)) + ((d - e) / f))
2. Match JSON object (nhiều cấp)
Biểu thức: \{(?:[^{}]|(?R))*\}
Phân tích:
+ \{: Bắt đầu với dấu ngoặc nhọn mở {.
+ (?:[^{}]|(?R))*
    Đây là phần quan trọng nhất:
    + [^{}] - match bất kỳ ký tự nào không phải { hoặc }.
    + | - hoặc
    + (?R) - đệ quy toàn bộ pattern hiện tại, nghĩa là tự gọi lại chính nó để xử lý JSON lồng nhau.
    + Dấu * - phần này có thể lặp lại nhiều lần, cho phép bên trong có các ký tự thường hoặc một JSON object khác lồng nhau.
+ \}: Kết thúc với dấu ngoặc nhọn đóng }.
Ví dụ: Nhận dạng chuỗi JSON lồng nhau
{
  "company": "ABC Corp",
  "employees": [
    {
      "id": 1,
      "name": "Nguyen \"Van\" A",
      "roles": ["admin", "editor"],
      "profile": {
        "age": 30,
        "contacts": {
          "email": "a.nguyen@example.com",
          "phones": ["0912345678", "0987654321"]
        }
      }
    },
    {
      "id": 2,
      "name": "Tran B",
      "roles": ["user"],
      "profile": {
        "age": 25,
        "contacts": {
          "email": "b.tran@example.com",
          "phones": []
        }
      }
    }
  ],
  "settings": {
    "theme": "dark",
    "features": {
      "beta": true,
      "modules": {
        "moduleA": {
          "enabled": true,
          "config": {
            "maxUsers": 100,
            "timeouts": [30, 60, 90]
          }
        }
      }
    }
  }
}
F. Thao tác, cleanup & trick (replace, performance, pitfalls)
1. Match tất cả dòng trùng lặp trong file (simple):  
Biểu thức: ^(.*)(\r?\n\1)+$
Mô tả: bắt nhóm dòng lặp liên tiếp; Replace bằng $1 giữ lại 1 bản.
Ghi chú: bật multiline, dùng Replace all.
2. Match tiền VNĐ (định dạng 1.200.000đ hoặc 2000000 VNĐ): 
Biểu thức: \b\d{1,3}(?:\.\d{3})*(?:đ|VNĐ)\b
Mô tả: hỗ trợ dấu . phân cách nghìn & suffix đ/VNĐ.
3. Match từ dài hơn 15 ký tự
Biểu thức: \b\w{15,}\b
Mô tả: Nhận dạng những mã thông báo dài đáng ngờ (băm, mã định danh dài).
4.  Match base64 dài > 100 ký tự
Biểu thức: (?:[A-Za-z0-9+\/]{4}){25,}(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?
Mô tả: Số block {25,} tương đương ~100+ chars base64.