Break down Regex

Mở đầu

Trong thời gian gần đây mình mới làm quen với regex, cảm thấy regex rất tiện lợi, đa dụng và được hỗ trợ nhiều ngôn ngữ (Bash,C,C++,java,PHP,Golang....).Nếu bạn đã từng gặp vấn đề như định dạng số điện thoại của người dùng nhập vào là xx-xx-xx-xx ,xxxx-xxxx,xx-xxxxxxx.... và muốn chuyển tất cả về 1 dạng chuẩn hoặc cần lấy các thẻ link trong html mà không cần viết nguyên cả 1 bộ parser thì bài viết này sẽ giúp bạn.Các bạn có thể chạy thử code sample tại đây

Các Thành phần cơ bản của regex

Matching String

Cơ bản nhất của regex đó là search trong chuỗi đầu vào có hay không có string mà mình định nghĩa.

<?php
$Regex_Pattern = '/typhoon/';
$handle = fopen ("php://stdin","r");
$Test_String = "typhoon is strong outside! typhoon! typhoon! run! run!";

preg_match_all($Regex_Pattern, $Test_String, $output_array);
printf("Number of matches : %d",count($output_array[0]));
fclose($handle);
?>
Number of matches : 3

regex cơ bản: \dot,\d,\w,\s

đây chính xác là những thứ cơ bản tiếp theo để bạn có thể viết được 1 regex đơn giản.Hãy tham khảo bảng dưới đây.

No ký hiệu ý nghĩa
1 . thay thế bất cứ thứ gì trong match string
2 \d viết tắt của digit, ký hiệu này sẽ tương đương với 1 digit trong match string
3 \D tương đương với 1 ký tự không phải là digit
4 \w viết tắt của word, ký hiệu này sẽ tương đương với 1 chữ cái trong match string
5 \W tương đương với 1 ký tự không phải là chữ
6 \s tương đương với 1 khoảng trắng có thể là 1 trong \r\n\t\f
7 \S tương đương với 1 ký tự không phải là khoảng trắng

ví dụ: bạn cần phải check 1 loại mã hàng hóa bắt đầu bằng chữ rồi đến space rồi đến mã 5 số rồi kết thúc bằng 1 ký tự bất kỳ.

 $Regex_Pattern = '/\w\s\d{5}\./';

chú ý: khi bạn dùng java thay vì dùng \ thì hãy dùng \
{x} cho phép ký tự trước nó lặp lại chính xác x lần.

 $Regex_Pattern = "/\\w\\s\\d{5}\\./";

Start và End

Một vấn đề thường gặp nữa là tìm string có quy định cho bắt đầu và kết thúc .Regex hỗ trợ ký tự ^ để tìm với bắt đầu và $ để tìm với chuỗi kết thúc ngay trước nó. ví dụ: với các đầu vào là tên nhân viên dạng : họ tên đệm tên. yêu cầu 1: Tìm tất cả những nhân viên có họ là Nguyen

 $Regex_Pattern = '^Nguyen';

yêu cầu 2: Tìm tất cả những nhân viên có tên là Nguyen

 $Regex_Pattern = 'Nguyen$';

Các kiểu lặp trong regex

Regex cung cấp cơ chế cho phép bạn tìm kiếm ký tự lặp lại 1 số lần nhất định ,lặp trong 1 khoảng giá trị và lặp với 1 số lần không xác định .Có 4 kiểu dưới đây.

  1. Lặp số lần nhất định {x}: cho phép ký tự ngay trước {x} trong regex parttern lặp đúng x lần ví dụ: xác định 1 số có phải là số điện thoại 10 số hay không.
 $Regex_Pattern = '^\d{10}$';
  1. Lặp số lần trong khoảng giá trị cho phép {x,y}: cho phép ký tự ngay trước {x,y} trong regex parttern lặp >=x và <=y lần: ví dụ: xác định 1 số có phải là số điện thoại 10 số hoặc 11 số hay không.
 $Regex_Pattern = '^\d{10,11}$';

chú ý: y có thể bỏ trắng. 3. Lặp từ 0 đến tùy ý (*): tương đương với {0,} ví dụ: check mã hàng bắt đầu bằng BCS và mã sản phẩm là 1 chuỗi số với độ dài bất kỳ [0,n]

 $Regex_Pattern = '^BCS\d*$';
  1. Lặp từ 0 đến tùy ý (+): tương đương với {1,} ví dụ: check mã hàng bắt đầu bằng BCS và mã sản phẩm là 1 chuỗi số với độ dài bất kỳ [0,n]
 $Regex_Pattern = '^BCS\d+$';

lựa chọn trong regex

Khi bạn gặp vấn đề 1 ký tự vừa có thể là space hoặc có thể là digit ,hoặc ký tự đó chỉ có thể là phụ âm thì bạn có thể áp dụng 1 trong 2 kiểu dưới đây. kiểu 1 (x|y): cho phép ký tự đó có thể là x hoặc y. bạn có thể thay thế x,y bằng các regex cơ bản \dot,\d,\w,\s. ví dụ: xác định chuỗi hoặc có 3 số hoặc có 5 chữ

 $Regex_Pattern = '^(\d{3}|\w{5})$';

kiểu 2 []: cho phép options là các ký tự nằm trong [] ví dụ: tìm những chuỗi chỉ bao gồm các số lẻ

 $Regex_Pattern = '^[13579]+$';

ở kiểu 2 có support khoảng giá trị trong dấu [] ví dụ:

 $Regex_Pattern = '^[1-5A-D]+$';

thì chuỗi có thể bao gồm các số 12345 và ABCD. Một vài ví dụ thông dụng như dưới đây: [a-zA-Z] tương đương \w [0-9] tương đương \d

Grouping regex

boudary (\b)

cho phép tìm kiếm các từ trong 1 string dài theo phạm vi:

  1. header boudary: ví dụ: \bdog sẽ tìm kiếm xem trong string có bất kỳ từ nào bắt đầu bằng dog và ngay sau nó là ký tự (\w) hay không match string : there are 3 dogs in box.
<?php
$Regex_Pattern = "/\bdog/";
$Test_String = "there are 3 dogssss in box.";
if(preg_match($Regex_Pattern, $Test_String, $output_array)){
    print ("true");
} else {
    print ("false");
}
?>
  1. tail boudary: ví dụ: \bdog sẽ tìm kiếm xem trong string có bất kỳ từ nào kết thúc bằng dog và ngay trước nó là ký tự (\w) hay không. match string : there are 3dog in box. not match : there are 3 dogs in box.
<?php
$Regex_Pattern = "/dog\b/";
$Test_String = "there are 3dog in box.";
//$Test_String = "there are 3 dogs in box.";  // test for not match
if(preg_match($Regex_Pattern, $Test_String, $output_array)){
    print ("true");
} else {
    print ("false");
}
?>
  1. middle boudary: ví dụ:\bdog\b sẽ tìm kiếm xem trong string có từ nào thỏa mãn cả head boudary + tail boudary hay không. Hoặc là từ đó dạng \Wdog\w hoặc \wdog\W. match string: there are dogss ,there are 3dog ? not match : ssdogss

Grouping to search ()

ví dụ có 1 cái thẻ html như sau:

<a href="http://www.phim3s.com"> phim hay </a>

bây giờ cần lấy ra giá trị của đường link và value của thẻ a dưới dạng http://www.phim3s.com,phimhay chúng ta sẽ dùng (.) để regex nhận được đâu là phần group cần được match để lấy được 2 giá trị đó ta cần định nghĩa 2 group trong regex như sau : regex = ".?<a href="(.?)".?>(.)</a>.?"; tùy theo ngôn ngữ sẽ có thư viện regex hỗ trợ khác nhau trong ví dụ này mình dùng C++14 cho mới mẻ :v.

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <regex>
using namespace std;
int main() {
     std::regex e (".*?<a href=\"(.*?)\".*?>(.*)</a>.*?");
     std::smatch sm;
     string s;
        cin.ignore();
        getline(cin,s);
 //       cout << s << endl;
        std::regex_match (s,sm,e);
        if(sm.size()==0){
        }
        else{
            cout << sm[1] << "," << sm[2] << endl;
        }
    return 0;
}

regex reference

có đủ 10 helpful sẽ bổ sung phần này 😄.

Kết luận

Khi mới nhìn thì regex là 1 đống dây xích lộn xộn nhưng thực ra tất cả đều được xây dựng từ những thành phần cơ bản nhất ,khi đã nắm được quy luật thì bạn sẽ làm chủ được công cụ khá cool này. Có khá nhiều tài liệu viết về regex nhưng thường không đầy đủ ,nhảy cóc khiến người đọc khó hiểu ,khó áp dụng.Bài viết trên của mình tóm tắt từ nhiều nguồn nhóm và phân loại lại theo cách hiểu của mình hi vọng giúp được những bạn mới bắt đầu hoặc không có hệ thống về regex.