Biểu thức chính quy - Regular expression

Biểu thức chính quy là gì?

Biểu thức chính quy là một nhóm các ký tự, ký hiệu viết ra theo quy luật tạo thành mẫu (pattern), nó được sử dụng để tìm kiếm văn bản (text).

Một biểu thức chính quy là một mẫu (pattern) nó tương đồng quy luật với một chuỗi từ trái qua phải. Biểu thức chính quy tên tiếng anh là Regular Expression gọi tắt là regex hoặc regexp

Trong lập trình nó được dùng với các hàm xử lý chuỗi, xử lý văn bản với các tác vụ cụ thể như: tìm và thay thế chuỗi, kiểm tra tính hợp lệ của dữ liệu, trích xuất chuỗi con từ một chuỗi ... Hầu hết các ngôn ngữ lập trình đều hỗ trợ (Biểu thức chính quy trong PHP, trong Java, trong C# ...) PHP, C#, JAVA ... Thậm chí RegExp còn rất phổ biến trong các ứng dụng, công cụ khác nhau như rewrite URL mod_rewrite, tìm kiếm và thay thế trong các IDE, truy vấn lọc dữ liệu trong một số dịch vụ của Google ...

Thử xem xét một ví dụ về Regex:

Giả sử ứng dụng của bạn yêu cầu tên người dùng phải tuân thủ quy tắc:

  • (1) Tên được phép chứa các ký tự, các số, gạch dưới, gạch nối.
  • (2) Tên phải có độ dài trong khoảng cho phép từ 3 đến 15 ký tự.

Thì biểu thức chính quy biểu diễn quy tắc đó sẽ như sau:

^[a-z0-9_-]{3,15}$

  • ^ : Ký hiệu cho biết bắt đầu một dòng
  • [a-z0-9_-] : Cho biết là tập tự: a-z, số từ 0 - 9, ký tự -, ký tự _
  • {3,15} : Chuỗi dài 3 đến 15 ký tự
  • $ : Điểm kết thúc dòng
  • Với mẫu như trên, chuỗi phù hợp đọc từ trái qua phải thì: bắt đầu một dòng - đến tập ký tự chỉ chứa (a-z, 0-9, -, _), chuỗi này dài 3 - 15, cuỗi cùng là kết thúc dòng.

    Với biểu thức chính quy trên thì các tên như xuanthu, xuan-xhu, xuanxhu_123 ... được chấp nhận phù hợp với mẫu. Còn mẫu không phù hợp như ab (nhỏ hơn 3 ký tự), abc@dx (chứa ký tự không cho phép) ...

    Biểu thức RegExp cơ bản

    Xét trường hợp đơn giản nhất, một biểu thức Regex chỉ là một mẫu là chuỗi các ký tự dùng để tìm kiếm trong văn bản. (chuỗi, text, string). Ví thụ một biểu thức RegEx ước, thì phù hợp với nó là bắt đầu bằng ư theo sau là và tiếp theo là c, mang biểu thức đó so khớp với đoạn text (văn bản) thì thấy hợp mẫu như sau:

    ước => Ước một điều ... mộng ước rất đơn sơ. Nụ hôn trao hạnh phút đến bất ngờ
    (Chạy thử vidu01)

    Biểu thức chính quy Regex là phân biệt chữ hoa chữ thường. Có cơ chế bật cờ thiết lập không phân biệt chữ hoa, chữ thường ở phần dưới

    Các ký tự biểu diễn thông tin siêu dữ liệu - Meta

    Các ký hiệu biểu diễn thông tin (meta character), được dùng để viết biểu thức chính quy RegEx, tóm tắt lại và giải thích chi tiết từng mục ở phần sau:

    Nếu có thể, sau khi đọc và hiểu các giải thích ở phần sau, hãy cố gắng nhớ bảng này, nó sẽ giúp bạn đọc hiểu và viết biểu thức chính quy được dễ dàng.

    Ký tự Meta Mô tả
    . Ký hiệu chấm. Biểu diễn bất kỳ ký tự nào ngoài trừ ký tự xuống dòng
    [ ] Mô tả một tập hợp các ký tự, các mẫu.
    [^ ] Tập hợp ký tự phủ định. Phù hợp nếu không có ký tự nào trong [^ ]
    * Lặp lại 0 đến nhiều lần.
    + Lặp lại 1 hoặc nhiều lần
    ? Tùy chọn có hay không cho mẫu phía trước
    {n,m} Độ dài tối thiểu là n tối đa là m
    (xyz) Biểu diễn một nhóm ký tự (có xét đến thứ tự của các ký tự).
    | Biểu diễn thay thế, (phép toán or, hoặc)
    \ Dùng để biểu diễn ký tự đặc biệt [ ] ( ) { } . * + ? ^ $ \ |
    ^ Điểm bắt đầu của dòng.
    $ Điểm kết thúc của dòng

    Ký hiệu chấm .

    Ký hiệu dấu chấm . là một meta đơn giản, nó biểu diễn bất kỳ ký tự nào ngoài trừ ký tự return \r hoặc newline \n. Ví dụ biểu thức RegEx .oàn thì có nghĩa là: một ký tự nào đó, tiếp theo đến ký tự o, tiếp theo đến à cuối cùng là n. Ví dụ dùng mẫu đó tìm trong chuỗi.

    .oàn =>Sự hoàn hảo dường như không thể đạt được, nhưng nếu chúng ta theo đuổi sự hoàn hảo
    thì chúng ta sẽ chạm đến sự xuất sắc.
    (Chạy thử vidu02)
    

    Cứ có 4 ký tự liên tiếp, bắt đầu bằng ký tự nào đó, sau đó là oàn thì phù hợp với mẫu.

    Tập hợp ký tự []

    Dùng [] để chứa tập hợp các ký tự viết ở bên trong nó, như tập [abc], phù hợp với nó là bất kỳ ký tự nào có trong tập đó. Có thể dùng dấu - để biểu diễn một dải các ký tự theo vị trí trong bảng chữ cái như a-z (ký tự thường từ a đến z), 0-9 (số từ 0 đến 9), A-Z (chữ in từ A đến Z) ..., biểu thức so sánh sẽ hợp mẫu nếu chứa bất kỳ ký tự nào trong đó (không cần quan tâm thứ tự)

    Ví dụ biểu thức [ưƯ]ớc có nghĩa là: Có một chữ ư hoặc Ư, theo sau bởi , tiếp theo là c

    [ưƯ]ớc => Ước một điều ... mộng ước rất đơn sơ. Nụ hôn trao hạnh phút đến bất ngờ
    (Chạy thử vidu03)
    

    Lưu ý: Viết [.] thì nó biểu diễn ký tự chấm . chứ không con ý nghĩa đại diện bất kỳ ký tự nào như trường hợp trên.

    nh[.] => Thời gian cứ thế xoay vòng thật nhanh. Bao mùa chiếc áo phông phanh!
    Chạy thử vidu04
    

    Mẫu phù hợp là 3 ký tự liên tiếp: nh.

    Tập hợp ngoại trừ [^]

    Thông thường thì ^ biểu diễn điểm bắt đầu của chuỗi, tuy nhiên nếu nó nằm ở vị trí sau dấu [ của cặp [] thì nó lại mang ý nghĩa tạo ra tập hợp ký tự loại trừ (phụ định). Ví dụ biểu thức [^n]hanh có nghĩa là bất kỳ ký tự nào ngoại trừ ký tự n, theo sau bởi h, tiếp theo bởi a, nh

    [^n]hanh => Thời gian cứ thế xoay vòng thật nhanh. Bao mùa chiếc áo phông phanh!
    Chạy thử vidu05
    

    Lặp lại với ký hiệu *

    Ký hiệu * cho biết có sự lặp lại 0 hoặc nhiều lần mẫu phù hợp đứng phía trước nó (như ký tự phía trước, tập [] phía trước, nhóm () phía trước ...). Ví dụ mẫu xuanthula*b thì sau ký tự l có 0 hoặc nhiều ký tự a liên tiếp rồi đến ký tự b. Nếu * nó đi sau tập hợp thì lặp tập hợp đó lặp lại 0 hoặc nhiều lần. Ví dụ [a-z]* là dòng có 0 hoặc n ký tự viết thường.

    * có thế sử dụng với dấu chấm .* để biểu diễn bất kỳ chuỗi nào, hay dùng mẫu (.*)
    * có thể sử dụng với ký tự trắng (trong RegEx khoảng trắng viết là \s) để biểu diễn bất kỳ khoảng trắng nào.
    Ví dụ \s*mình\s* có nghĩa bắt đầu bởi không hoặc nhiều khoảng trắng, tiếp theo là ký tự m, ì, n, h tiếp theo là không hoặc nhiều khoảng trắng.

    "\s*mình\s*" => Đừng so sánh mình với bất cứ ai trong thế giới này.
    Nếu bạn làm như vậy có nghĩa bạn đang sỉ nhục chính bản thân mình. Bill Gates
    Chạy thử vidu06
    

    Lặp lại với ký hiệu +

    Ký hiệu + tương tự như * nhưng lặp lại 1 hoặc nhiều (ít nhất một lần). Ví dụ
    có.+! có nghĩa ký tự bắt đầu bằng theo sau ít nhất một ký tự nào đó (một hoặc nhiều ký tự), cuối cùng là ký tự !.

    có.+! => Đàn ông cần tiền chủ yếu chỉ để cho hai việc: có được nàng và thoát được nàng!.
    Chạy thử vidu07
    

    Mẫu phía trước có hay không đều được với ?

    Trong biểu thức Regex thông thường ? là một tùy chọn cho biết mẫu phía trước nó có thể có hoặc không. Ví dụ [h]?ôn nghĩa là tùy chọn có h hoặc không, theo sau là ô, tiếp theo là n

    [h]?ôn => Đàn bà khôn ngoan hơn đàn ông vì họ biết ít hơn, nhưng hiểu nhiều hơn.
    Chạy thử vidu08
    

    Biểu diễn độ dài {}

    {} là biểu diễn số lượng, nó chỉ ra số lần mà một ký tự hoặc một nhóm các ký tự lặp lại. Viết {min, max} có nghĩa số lượng tối thiểu min và tối đa max. Ví dụ [0-9]{2,3} có nghĩa là có tối thiểu 2 tới 3 ký tự số.

    Nếu bạn viết {min,} có nghĩa tối thiểu có min lần lặp lại (số lượng min). Ví dụ [0-9]{2,} có nghĩa là chuỗi có 2 hoặc nhiều ký tự số.

    Nếu viết {number} thì biểu diễn chính xác số lần lặp lại. Ví dụ [0-9]{3} có nghĩa là chuỗi chính xác có 3 ký tự số.

    Nhóm mẫu (...)

    Nhóm là một mẫu (pattern) viết trong () ví dụ (ab). Thường dùng nhóm như là một mẫu con trong một mẫu lớn. Ví dụ (ab)*: thì (ab) là mẫu con, trong mẫu lớn thì nó lặp lại 0 hoặc nhiều lần => lặp lại ab 0 hoặc nhiều lần.

    Chúng ta cũng dùng ký hiệu | bên trong nhóm như là phép toán or (hoặc) để xác định nhóm. Ví dụ n(g|h) có nghĩa bắt đầu bằng n theo sau là một mẫu con, mẫu đó hoặc là chữ g hoặc là chữ h. Thế thì các chuỗi nhỏ như nh, ng thì phù hợp.

    n(g|h) =>Nếu có một ai đó làm chậm bước chân của bạn, hãy nhnhàng rẽ sang hướng khác.
    Chạy thử vidu09
    

    Viết mẫu như trên còn gọi là capture group vì các ngôn ngữ lập trình triển khai RegEx sẽ nhớ thông tin mẫu con này trong thông tin kết quả trả về.

    Nếu chỉ cần lấy kết quả mẫu chính (không quan tâm mẫu con trả về, mặc dù phải phù hợp với mẫu con thì mẫu chính mới phù hợp) thì cho thêm ký hiệu ?: sau ký hiệu (. Ví dụ: n(?:g|h)

    Phân biệt capture group và non capture group thực ra cần xét trong một ngôn ngữ lập trình cụ thể, lấy ví dụ Javascript (bạn có thể chạy ngay trong trình duyệt để kiểm tra).

    <script>
        // CAPTURE
        // Tạo biểu thức chính quy với mẫu là: n(g|h)
        // Sau đó so mẫu với chuỗi: 'hướng khác'
    
        const regex = new RegExp('n(g|h)');
        const  kq = regex.exec('hướng khác');
        console.log(kq);
    
        // mảng kq:
        /*
            ['ng', 'g', index: 3, input: 'hướng khác', groups: undefined]
         */
    </script>
    

    Trong mảng kết quả trả về, có chuỗi phù hợp với mẫu lớn tìm được là ng, nó cũng cho biết luôn tương là mẫu con g.

    <script>
        // NON CAPTURE
        // Tạo biểu thức chính quy với mẫu là: n(?:g|h)
        // Sau đó so mẫu với chuỗi: 'hướng khác'
    
        const regex = new RegExp('n(?:g|h)');
        const  kq = regex.exec('hướng khác');
        console.log(kq);
    
        // mảng kq:
        /*
            ['ng', index: 3, input: 'hướng khác', groups: undefined]
         */
    </script>
    

    Kết quả trả về tìm được ng phù hợp với mẫu lớn, nhưng không chứa thông tin mẫu con (không nhớ).

    Biểu diễn thay thế |

    Ký tự | chính là phép toán hoặc (or) kết hợp giữa các mẫu con, như (a|b|c), abc|xyz, (pattern1)|(pattern2) ... Xem các ví dụ phía trên, đã sử dụng |

    Biểu diễn ký tự đặc biệt với \

    Do một số ký hiệu đã được dùng đã biểu diễn Regex như : { } [ ] / \ + * . $ ^ | ? nên để biểu diễn các ký tự đó dùng ký hiệu \ trước ký tự.

    "(f|c|m)at\.?" => The fat cat sat on the mat.

    Bắt đầu của dòng ^

    Sử dụng ^ để kiểm tra các ký tự đàu tiên của dòng phù hợp với mẫu viết sau ^. Ví dụ ^a thì chuỗi phù hợp khi bắt đầu là chữ a có dạng như abcxyz, nếu vẫn chuỗi đó nó lại không phù hợp với ^b.
    ^(T|t)he có nghĩa là T hoặc t bắt đầu của chuỗi, theo sau là he.
    ^(Trần|Lê).* lấy là các dòng bắt đầu bằng Trần hoặc

    Điểm kết thúc của chuỗi $

    Cho biết kết thúc dòng phải thỏa mãn mẫu phía trước $

    Ngược lại với ^ ví dụ (at\.)$ nghĩa là cuối chuỗi có at. thì là phù hợp.
    "(at\.)$" => The fat cat. sat. on the mat.

    Ký hiệu tắt cho tập hợp

    Viết tắt Diễn tả
    . Bất kỳ ký tự nào ngoại trừ xuống dòng
    \w Chữ,sô, và _, tương đương với: [a-zA-Z0-9_]
    \W Ngoài bảng chữ cái, tương đương với: [^\w]
    \d Các số: [0-9]
    \D Không phải số: [^\d]
    \s Là ký tự trắng, tương đương với: [\t\n\f\r\p{Z}]
    \S Không phải ký tự trắng: [^\s]

    Biểu thức ?= lookahead (?=...)

    Một biểu thức con viết bắt đầu bằng ?= gọi là lookahead (là một loại non-capture, chỉ để khớp mẫu, không nhớ kết quả). Biểu thức lookahead cho vào để lọc kết quả phía trước nó, kết quả trả về (biểu thức phía trước) là phần đầu của mẫu khớp - phần sau không trả về phải thỏa mãn lookahead.

    Ví dụ (T|t)he(?=\sfat) thì lookahead(?=\sfat) - nghĩa là kết quả trả về khi ở trong chuỗi gốc nó được theo sau bởi fat


    Ví dụ, lấy ra từ The hoặc the

    (T|t)he => The fat cat sat on the mat.
    Chạy thử vidu10
    

    Lấy ra từ The hoặc the, nhưng từ đó phải đứng trước từ fat

    (T|t)he(?=\sfat) => The fat cat sat on the mat.
    Chạy thử vidu11
    

    Biểu thức ?! phủ định lookahead (?!...)

    Biểu thức (?! ...) là lấy ra kết quả, mà kết quả này ở trong chuỗi gốc phía sau nó không được thỏa mãn biểu thức viết trong (?! ...)

    Ví dụ lấy ra từ The hoặc the, nhưng từ đó trong chuỗi gốc đằng sau không có fat

    (T|t)he(?!\sfat) => The fat cat sat on the mat.
    Chạy thử vidu12
    

    Biểu thức (?<=...) Lookbehind

    Cũng là loại non-capture, sử dụng đề lọc kết quả phía sau nó. Kết quả trả về trong chuỗi gốc, phía trước nó phải thỏa mãn lookbehind. (?<=(T|t)he\s)(fat|mat) có nghĩa lấy tất cả các từ fat hoặc mat sau các từ The hoặc the

    Lấy ra các từ mat hoặc cat, nhưng trong chuỗi gốc - phía trước nó phải có The hoặc the

    (?<=(T|t)he\s)(fat|mat) => The fat cat sat on the mat.
    Chạy thử vidu13
    

    Biểu thức (?<!...) phủ định Lookbehind

    Sử dụng để lấy các phù hợp mà đi trước không có một mẫu lookbehind chỉ ra.

    (?<!(T|t)he\s)(cat) => The cat sat on cat.
    Chạy thử vidu14
    

    Các cờ

    Cờ Diễn tả
    i Thiết lập không phân biệt chữ hoa chữ thường
    g Tìm toàn bộ kết quả (không dừng lại khi thấy kết quả đầu tiên).
    m Xử lý nhiều dòng (khi chuỗi input nhiều dòng, mỗi dòng được mang ra khớp mẫu).

    Các cờ này được đưa vào mẫu theo dạng /RegExp/flags

    "/The/gi" => The fat cat sat on the mat.


    "/.(at)/" => The fat cat sat on the mat.

    "/.(at)/g" => The fat cat sat on the mat.

    "/.at(.)?$/gm" => The fat
    cat sat
    on the mat.

    Trong PHP các hàm sử dụng RegExp thường các biểu thức này nằm trong cặp /../

    Tóm tắt

    Ký hiệu Mô tả
    . Ký tự bất kỳ.
    [ ] [..] Mô tả một tập hợp các ký tự, các mẫu. Còn [^...] là phủ định của [], phù hợp nếu không có ký tự nào trong [^ ].
    Đặc biệt:
    • \w[a-zA-Z0-9_] (từ)
    • \W[^\w][^a-zA-Z0-9_]
    • \d[0-9]
    • \D[^0-9][^d]
    • \s[\t\n\f\r\p{Z}] (các ký tự trắng tab, space, return ...
    • \S[^s] (không phải ký tự trắng)
    • \b ⥂ phân cách các từ
    • \B ⥂ không phải phân cách các từ
    [^ ]
    * Lặp lại 0 đến nhiều lần.
    + Lặp lại 1 hoặc nhiều lần
    ? Tùy chọn có hay không cho mẫu phía trước đều được.
    ?!... Lookahead. (non-capture) Kết quả trả về khi ở trong chuỗi gốc, nó đứng trước chuỗi thỏa mãn lookahead.
    {min,max} Độ dài. {min, } độ dài tối thiểu, {number} độ dài chính xác.
    (xyz) Capture group. Biểu diễn nhóm mẫu và nhớ kết quả trả về.
    | Thay thế (phép toán or, hoặc)
    \ Dùng để biểu diễn ký tự đặc biệt [ ] ( ) { } . * + ? ^ $ \ |
    ^ Điểm bắt đầu của dòng.
    \A Điểm bắt đầu của chuỗi input.
    $ Điểm kết thúc của dòng
    \z Điểm kết thúc chuỗi input

    Luyện tập phân tích với một số biểu thức chính quy thông dụng

    Một số mẫu để kiểm tra dữ liệu, tìm kiếm, trích xuất ...

    Địa chỉ email

    ^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})*/$

    • [a-zA-Z0-9._%-]+: phần đầu - phải có một chuỗi hình thành từ các ký trong mẫu
    • @: tiếp theo đến ký tự @
    • [a-zA-Z0-9.-]+: tiếp theo phải có một chuỗi hình thành bởi các ký tự trong mẫu
    • \.: tiếp theo phải có ký tự .
    • [a-zA-Z]{2,4}: phần cuối có chuỗi dài 2 - 4 hình thành bởi các ký tự chữ cái.

    Đia chỉ IP4

    ^((?:(?: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]?))*$

    (Đang cập nhật)

    Tác giả: https://twitter.com/ziishaned


    Đăng ký nhận bài viết mới