Sử dụng Regular Expression

Sử dụng regular expression là một kiến thức cần có của các developer. Nó giúp giải các bài toán tìm kiếm, tách lọc dữ liệu theo mẫu chỉ định.

1. Regular expression là gì

Một regular expression (regex) là 1 mẫu (pattern) dùng để so khớp trong văn bản. Kiến thức về regular rất hữu ích trong các bài toán tìm, so khớp, bóc tách dữ liệu theo 1 mẫu nào đó.

2. Pattern  (mẫu)

Mỗi 1 mẫu (pattern) phải đặt trong dấu hiệu gọi là delimiter. Các delimiters thường dùng trong php là  /, # , ~ } .  Sau đây là một vài mẫu :

/abc/
#[0-9]{3}$#
+php+
%[a-zA-Z0-9_-]%

Nếu cần so khớp ký tự delimiter thì trong pattern thêm trước nó dấu \  hoặc dùng delimiter khác, ví dụ sau là 2 cách diễn tả pattern http://

/http:\/\//
#http://#

3. Các hàm php xử lý regular expression

a. Hàm preg_match

Hàm preg_match($pattern, $string)  Cho biết pattern có khớp (được tìm thấy) trong chuỗi hay không. Ví dụ:

<?php
  $kq1 = preg_match('/thân/', "Mẹ tôi hai tiếng thân thương");
  $kq2 = preg_match('/yêu/', "Mẹ tôi hai tiếng thân thương");
  $kq3 = preg_match('/ng$/', "Mẹ tôi hai tiếng thân thương");
  $kq4 = preg_match('/^Má/', "Mẹ tôi hai tiếng thân thương");
  if ($kq1) echo "Tìm thấy chữ thân<br>";   else echo "Không tìm thấy chữ thân<br>";
  if ($kq2) echo "Tìm thấy chữ yêu<br>";   else echo "Không tìm thấy chữ yêu<br>";
  if ($kq3) echo "Tận cùng là ng<br>";
  if ($kq4) echo "Bắt đầu là Má<br>";
?>

Kết quả:

Tìm thấy chữ thân
Không tìm thấy chữ yêu
Tận cùng là ng

b. Hàm preg_replace

Sử dụng regular expression để tìm và thay thế rất hay. Bạn sử dụng hàm preg_replace() Hàm này giúp bạn tìm pattern và thay thế bởi chuỗi mới. Ví dụ:

<?php
 $str ="
    Vân Tiên nghe nói động lòng,
    Đáp rằng: Ta đã trừ dòng lâu la.
    Khoan khoan ngồi đó chớ ra,
    Nàng là phận gái, ta là phận trai.
    Tiểu thư con gái nhà ai,
    Đi đâu nên nỗi mang tai bất kỳ";
   $str = preg_replace('/(ai)|(òng)/', "...", $str);
   echo "<pre>",$str;
?>

Kết quả:

    Vân Tiên nghe nói động l...,
    Đáp rằng: Ta đã trừ d... lâu la.
    Khoan khoan ngồi đó chớ ra,
    Nàng là phận gái, ta là phận tr....
    Tiểu thư con gái nhà ...,
    Đi đâu nên nỗi mang t... bất kỳ

c. Hàm preg_split

Sử dụng regular expression để tách liệu theo mẫu. Để thực hiện, bạn dùng hàm preg_split(), nó  giúp tách 1 chuỗi theo mẫu mà bạn chỉ định. Ví dụ:

<?php
$string = 'Mồi phú quí dữ làng xa mã, Bả vinh hoa lừa gã công khanh; Giấc Nam Kha khéo bất bình. Bừng con mắt dậy thấy mình tay không';
$pattern = '/[,;.]/';
$arr = preg_split($pattern, $string); print_r($arr);
echo "<br>",implode(" - ", $arr);
?>

Kết quả:

Array (
[0] => Mồi phú quí dữ làng xa mã
[1] => Bả vinh hoa lừa gã công khanh
[2] => Giấc Nam Kha khéo bất bình
[3] => Bừng con mắt dậy thấy mình tay không )

Mồi phú quí dữ làng xa mã – Bả vinh hoa lừa gã công khanh – Giấc Nam Kha khéo bất bình – Bừng con mắt dậy thấy mình tay không

4. Dấu chấm trong pattern

Dấu . để diễn tả 1 ký tự bất kỳ ngoại trừ ký tự newline.

5. Khớp pattern ở đầu và cuối chuỗi

Dấu ^ khớp với vị trí ký tự đầu tiên trong chuỗi hoặc đầu dòng. Chẳng hạn với chuỗi abc, thì pattern ^a là khớp còn pattern ^b thì không khớp .

Dấu $ khớp với vị trí ký tự cuối cùng hoặc cuối dòng. Với chuỗi abc thì pattern  c$ sẽ khớp , còn pattern a$ thì không khớp.

6. Class trong pattern (lớp ký tự trong pattern)

Class trong pattern được bao bởi dấu [ ]  (ví dụ [ps] )  Class được dùng để so khớp với 1 ký tự trong chuỗi. Có nghĩa là khớp nếu có 1 ký tự  trong chuỗi khớp với 1 trong những ký tự của class.  Ví dụ để so khớp ký tự u hoặc o, thì diễn tả bằng class [ai].  Như vậy /th[ai]nh/ sẽ khớp với  thanh hoặc thinh

Trong class, dấu – để diễn tả một vùng liên tục, ví dụ [0-6]  diễn tả một digit từ 0 đến 6.  Còn đặt dấu ^ sau [ trong class sẽ phủ định class đó, nghĩa là sẽ so khớp với mọi ký tự không nằm trong class đó. Lớp Ví dụ  a[^m]  nghĩa là “a theo sau bởi 1 ký tự không phải m”. Vì vậy sẽ không khớp trong chuỗi Tam

Chú ý:

  • Thứ tự ký tự trong class không quan trọng [ps] và [sp] là như nhau
  • ^  sẽ mang nghĩa thường nếu không đặt nó ngay sau [ . Ví dụ  [x^]  sẽ khớp với x hoặc ^.
  • ] sẽ mang nghĩa thường nếu đặt nó ở ngay sau [  hoặc  [^ . Ví dụ  []x] sẽ khớp với ] hoặc x.  [^]x] sẽ khớp với bất kỳ ký tự nào không phải là ] hoặc x.
  • –  sẽ mang nghĩa thường nếu đặt nó ở ngay sau [ hoặc [^  hoặc ngay trước ]. Ví dụ  [-x] và [x-] đều so khớp với – hoặc x.

7. Các cách viết tắt

  • \d là viết tắt của [0-9]
  • \w  để để diễn tả 1 từ
  • \s để để diễn tả 1 “khoảng trắng” tạo bởi space hoặc tab

Ví dụ \s\d diễn tả một ký tự trắng theo sau là một digit. [\s\d] khớp với ký tự trắng hoặc một digit.

8. Lặp lại class

Dấu ? * +  được dùng khi muốn lặp lại class. Dấu + để diễn tả từ 1 trở lên, * là từ 0 trỡ lên,? Là 1 ký tự. Ví dụ:

<?php
$str ="Tèo: 0918667788; Tý: 012777345; Lượm: 86927322";
$pattern="/[0-9]+/";
preg_match($pattern, $str, $arr); print_r($arr); echo "<br>";
preg_match_all($pattern, $str, $arr); print_r($arr);
?>

Kết quả:

Array ( [0] => 0918667788 )
Array ( [0] => Array ( [0] => 0918667788 [1] => 012375345 [2] => 86927302 ) )

9. Back references

Back reference được đặt bên ngoài class, back reference gồm dấu \ và 1 digit >0 , 1 back reference tương ứng với 1 capturing subpattern trước đó. 

10. Một số ví dụ

a. Ví dụ Tìm chuỗi con

<pre><?php
$str = 'Anh thương em Anh đi đâu đâu';
$regex = '/a[a-z][a-z] /';
if (preg_match($regex, $str) ==true) echo "Tìm thấy" ; else echo "Không thấy";
echo "<br>";
$regex = '/a[a-z][a-z] /i'; //không phân biệt hoa thường
if (preg_match($regex, $str) ==true) echo "Tìm thấy" ;else echo "Không thấy";
?>

b. Ví dụ Tách chuỗi con

<?php
$string = 'Mồi phú quí dữ làng xa mã.
Bả vinh hoa lừa gã công khanh.
Giấc Nam Kha khéo bất bình.
Bừng con mắt dậy thấy mình tay không.';
$pattern = '/\r\n/';
$a= preg_split($pattern, $string);
print_r($a);
?>

c. Ví dụ Tìm chuỗi unicode

<pre><?php
$str = 'Vạn lý thanh giang vạn lý thiên.
Nhất thôn tang giá, nhất thôn yên.
Ngư ông thuỵ trước vô nhân hoán.
Quá ngọ tỉnh lai tuyết mãn thuyền. ';
$regex = '/[lv]\pL\s/u';  // số thì dùng \N
preg_match_all($regex, $str,$b) ;
print_r($b); ?>

d. Ví dụ Tìm chuỗi con unicode       

<pre><?php
$string = 'Bóng gương thấp thoáng dưới mành
Cỏ cây cũng muốn nổi tình mây mưa
Chìm đáy nước cá lờ đờ lặn
Lửng da trời nhạn ngẩn ngơ sa
Hương trời đắm nguyệt say hoa
Tây Thi mất vía, Hằng Nga giật mình';
$pattern = '/ng\pL+\s/iu';
preg_match_all($pattern, $string, $e);
print_r($e);     
?>

e. Ví dụ Tìm và thay thế     

<pre><?php
$pattern = '/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/';     
$replace = '\4/\3/\1\2';
$str= 'Hôm nay là ngày : <b>2013-4-15</b>';
echo preg_replace($pattern, $replace, $str); //Hôm nay là ngày : 15/4/2013
?>

f. Ví dụ Tìm và thay thế   

<pre><?php
$str = 'Nguyễn     Văn     Tèo';
$str = preg_replace('/\s\s+/', ' ', $str);
echo $str; //Nguyễn Văn Tèo
?>

g. Ví dụ Tìm và thay thế   

<pre><?php
$str = 'Nguyễn  @   Văn  !   Tèo ';
$str= preg_replace('/[\s@!]/', ' ', $str,-1,$count);
echo $str , " , ", $count;  //Nguyễn Văn Tèo , 13
?>

h. Ví dụ Tìm số    

<pre><?php
$subject = "Ngày sinh: 15/04/1990";
$pattern = '/\d+/';
preg_match($pattern, $subject, $m);
print_r($m); //Array ( [0] => 15 )
?>

i. Ví dụ Tìm số và lấy ra dùng      

<pre><?php
$subject = "Ngày sinh: 15/04/1990";
$pattern = '/\d+/';
preg_match_all($pattern, $subject, $matches,PREG_SET_ORDER);
print_r($matches); echo "<br>";
echo $matches[2][0],"-", $matches[1][0],"-",$matches[0][0];
?>

j. Ví dụ Email valid     

<pre><?php
$mail1 = "abc123@lolhaha";  $mail2 = "teo@somesite.com"; 
$regex = '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/';
if (preg_match($regex, $mail1)) echo $mail1 . " là địa chỉ hợp lệ";
else  echo $mail1 . " là địa chỉ không hợp lệ";
echo "<br>";
if (preg_match($regex, $mail2))  echo $mail2 . " là địa chỉ hợp lệ";
else echo $mail2 . " là địa chỉ không hợp lệ";
?>
<br />
<?php
$mail3= "teonguyen.123@vbu.edu.vn";
$regex='/^[^\W][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)*\@[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)*\.[a-zA-Z]{2,4}$/';
if (preg_match($regex, $mail3)) echo "$mail3 là địa chỉ hợp lệ";
else echo "$mail3 là địa chỉ không hợp lệ";
?>

k. Ví dụ IP valid      

<pre><?php
$ip = "255.255.255.255";
$regex= '/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:[.](?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/';
if (preg_match($regex,$ip)) echo "Địa chỉ $ip là hợp lệ";
else "Địa chỉ $ip không hợp lệ"; ?> <hr/>

l. Ví dụ Phone valid     

<pre><?php
$phone = "(232) 555-5555";
$regex='/^\(?[0-9]{3}\)?|[0-9]{3}[-. ]? [0-9]{3}[-. ]?[0-9]{4}$/';
if (preg_match($regex, $phone)) echo "Số $phone hợp lệ";
else echo "Số $phone không hợp lệ";
?>

Bài Tập

Tạo file bai1.php và thực hiện

  • Dùng hàm file_get_contents để request trang https://vnexpress.net , kết quả lưu vào biến $str
  • Hiện code vừa nhận được (echo “<xmp>”,$str,”</xmp>”;)
  • Chạy thử trang web để xem kết quả.
  • Dùng hàm preg_match_all để lấy tất cả các chuỗi khớp với pattern sau đây rồi lưu vào biến array arr rồi hiện array ra trang web.
{ href="https://(.*)\.html" }'
  • Giải thích parttern

Tạo file bai2.php và thực hiện

Tương tự bài 1 nhưng lấy từ https://nld.com.vn, các chuỗi trong giá trị của src=”” (tức là địa chỉ các hình). Yêu cầu: chỉ lấy các hình có đuôi jpg  hoặc png

Tạo file bai3.php và thực hiện

  • Tiếp nhận giá trị của biến pass trong địa chỉ trang
  • Kiếm tra xem giá trị trong biến pass có phải là 1 mật khẩu mạnh hay không

Tạo file bai4.php và thực hiện

  • Tiếp nhận giá trị của biến phone trong địa chỉ trang
  • Kiếm tra giá trị biến phone có phải là 1 số điện thoại hợp lệ theo công thức 0xxx-xx-xx-xx  hay không