Các quy tắc Regular Expression căn bản (tiếp theo)
1. Regex đại diện cho một ký tự
Bài trước ta đã được học cách xác định phạm vi của một chuỗi bằng cách dùng cú pháp [min-max]
hoặc [list_char]
, nhưng giả sử tôi muốn một Regex chấp nhận một ký tự bất kì nào đó thì không thể sử dụng cú pháp đó được. Và thật may trong Regular Expression đã cho ta một cách đó là dùng ký tự .
để định nghĩa cho một kí tự bất kì.
Ví dụ:
1
2
3
4
5
6
// Pattern là ký tự bất kỳ dài từ 3 đến 10 ký tự
$pattern
=
'/^.{3,10}$/'
;
$subject
=
'3232'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
[min-max]
hoặc [list_char]
, nhưng giả sử tôi muốn một Regex chấp nhận một ký tự bất kì nào đó thì không thể sử dụng cú pháp đó được. Và thật may trong Regular Expression đã cho ta một cách đó là dùng ký tự .
để định nghĩa cho một kí tự bất kì.
1
2
3
4
5
6
| // Pattern là ký tự bất kỳ dài từ 3 đến 10 ký tự $pattern = '/^.{3,10}$/' ; $subject = '3232' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
2. Ký hiệu đặc biệt cho các từ khóa Regex
Giả sử giờ tôi cần kiểm tra một chuỗi có tồn tại dấu chấm .
hay không? Nếu áp dụng kí tự .
thì ta sẽ làm như sau:
1
2
3
4
5
6
// Kết quả ko mong đợi
$pattern
=
'/./'
;
$subject
=
'demo'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Chạy đoạn code này lên kết quả trả về true, điều này sai với kết quả ta mong đợi vì trong $subject không tồn tại dấu chấm .
. Lý do Regular Expression đã hiểu nhầm ký tự dấu chấm ở $pattern
là đại diện cho ký tự bất kì nên nó so khớp với $subject
là hoàn toàn đúng.
Những ký tự như dấu chấm .
, mở ngoặc và đóng ngoặc vuông []
, hoặc những ký tự liên quan đến quy tắc của Regular Expression đều được quy về dạng ký tự đặc biệt trong Regular Expression. Vì thế để phân biệt giữa ký tự đặc biệt Regex và ký tự bình thường thì ta thêm dấu \
vào đầu ký tự đó.
Như ở ví dụ trên tôi sẽ code lại như sau:
1
2
3
4
5
6
// Dấu chấm là ký tự đặc biệt trong regex nên phải thêm dấu \
$partern
=
'/\./'
;
$subject
=
'demo'
;
if
(preg_match(
$partern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Chạy lên kết quả như mong đợi đó là trả về FALSE
vì $subject = 'demo'
, nếu bạn đổi giá trị $subject = '.'
thì kết quả sẽ TRUE
.
.
hay không? Nếu áp dụng kí tự .
thì ta sẽ làm như sau:
1
2
3
4
5
6
| // Kết quả ko mong đợi $pattern = '/./' ; $subject = 'demo' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
.
. Lý do Regular Expression đã hiểu nhầm ký tự dấu chấm ở $pattern
là đại diện cho ký tự bất kì nên nó so khớp với $subject
là hoàn toàn đúng..
, mở ngoặc và đóng ngoặc vuông []
, hoặc những ký tự liên quan đến quy tắc của Regular Expression đều được quy về dạng ký tự đặc biệt trong Regular Expression. Vì thế để phân biệt giữa ký tự đặc biệt Regex và ký tự bình thường thì ta thêm dấu \
vào đầu ký tự đó.
1
2
3
4
5
6
| // Dấu chấm là ký tự đặc biệt trong regex nên phải thêm dấu \ $partern = '/\./' ; $subject = 'demo' ; if (preg_match( $partern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
FALSE
vì $subject = 'demo'
, nếu bạn đổi giá trị $subject = '.'
thì kết quả sẽ TRUE
.3. Regex A hoặc B
Giả sử tôi cần kiểm tra $subject = 'A'
hoặc bằng $subject = 'B'
sẽ trả về đúng thì ta dùng dấu |
, đây là kí hiệu biểu diễn mối quan hệ OR
.
1
2
3
4
5
$pattern
=
'/^A|B$/'
;
$subject
=
'A'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Kết quả sẽ trả về TRUE
. Bây giờ bạn thử thay đổi giá trị của $subject
và kiểm tra lại nhé.
$subject = 'A'
hoặc bằng $subject = 'B'
sẽ trả về đúng thì ta dùng dấu |
, đây là kí hiệu biểu diễn mối quan hệ OR
.
1
2
3
4
5
| $pattern = '/^A|B$/' ; $subject = 'A' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
TRUE
. Bây giờ bạn thử thay đổi giá trị của $subject
và kiểm tra lại nhé.4. Gom nhóm Regex lại
Đôi lúc ta muốn gom nhóm Regex lại cho dễ nhìn, việc này đơn giản ta chỉ cần đặt đoạn mã Regex bên trong cặp đóng và mở ()
. Khi sử dụng gom nhóm thì việc so khớp vẫn bình thường, tuy nhiên với kết quả về của biến $matches
thì sẽ có sự thay đổi và chi tiết thế nào thì ở phần Capturing Group dưới đây mình sẽ đề cập tới.
Ví dụ:
1
2
3
4
5
6
// Gom nhóm A hoặc B lại thành 1 nhóm
$pattern
=
'/(A|B)/'
;
$subject
=
'A'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
()
. Khi sử dụng gom nhóm thì việc so khớp vẫn bình thường, tuy nhiên với kết quả về của biến $matches
thì sẽ có sự thay đổi và chi tiết thế nào thì ở phần Capturing Group dưới đây mình sẽ đề cập tới.
1
2
3
4
5
6
| // Gom nhóm A hoặc B lại thành 1 nhóm $pattern = '/(A|B)/' ; $subject = 'A' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
5. Regex kiểm tra chiều dài không giới hạn
Ở bài trước ta đã được học để xác định chiều dài cho chuỗi Regex $pattern
thì dùng cú pháp {min,max}
, tuy nhiên vẫn còn một số quy tắc ngắn gọn hơn đó là sử dụng các ký tự *
, +
, ?
để thiết lập chiều dài cho chuỗi.
$pattern
thì dùng cú pháp {min,max}
, tuy nhiên vẫn còn một số quy tắc ngắn gọn hơn đó là sử dụng các ký tự *
, +
, ?
để thiết lập chiều dài cho chuỗi.
Ký tự *
Đại diện cho không hoặc nhiều ký tự.
Ví dụ: kiểm tra chuỗi có phải trống hoặc là gồm các chữ cái thường.
1
2
3
4
5
6
// Chuỗi có phải trống hoặc có những chữ cái in thường
$pattern
=
'/[a-z]*/'
;
$subject
=
'dsada'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Với bài này ta cũng có thể giải cách sau:
1
2
3
4
5
6
// Chuỗi có phải trống hoặc có những chữ cái in thường
$pattern
=
'/[a-z]{0,}/'
;
$subject
=
's'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
1
2
3
4
5
6
| // Chuỗi có phải trống hoặc có những chữ cái in thường $pattern = '/[a-z]*/' ; $subject = 'dsada' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
1
2
3
4
5
6
| // Chuỗi có phải trống hoặc có những chữ cái in thường $pattern = '/[a-z]{0,}/' ; $subject = 's' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
Ký tự +
Đại diện cho 1 hoặc nhiều ký tự.
Ví dụ: kiểm tra chuỗi có ít nhất một chữ thường
1
2
3
4
5
6
// chuỗi ít nhất có 1 ký tự chữ thường
$pattern
=
'/[a-z]+/'
;
$subject
=
's'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Với bài này ta có thể giải bằng cách sau:
1
2
3
4
5
6
// chuỗi ít nhất có 1 ký tự chữ thường
$pattern
=
'/[a-z]{1,}/'
;
$subject
=
's'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
1
2
3
4
5
6
| // chuỗi ít nhất có 1 ký tự chữ thường $pattern = '/[a-z]+/' ; $subject = 's' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
1
2
3
4
5
6
| // chuỗi ít nhất có 1 ký tự chữ thường $pattern = '/[a-z]{1,}/' ; $subject = 's' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
Ký tự ?
Đại diện cho một hoặc không có ký tự nào
Ví dụ:
1
2
3
4
5
6
// chuỗi có 1 hoặc không có ký tự thường nào
$pattern
=
'/[a-z]?/'
;
$subject
=
's'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
Bài này ta có thể giải bằng cách sau:
1
2
3
4
5
6
// chuỗi có 1 hoặc không có ký tự thường nào
$pattern
=
'/[a-z]{0,1}/'
;
$subject
=
's'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
1
2
3
4
5
6
| // chuỗi có 1 hoặc không có ký tự thường nào $pattern = '/[a-z]?/' ; $subject = 's' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
1
2
3
4
5
6
| // chuỗi có 1 hoặc không có ký tự thường nào $pattern = '/[a-z]{0,1}/' ; $subject = 's' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
6. Regex phủ định - NOT
Ta dùng ký tự ^
để phủ định một Regex nào đó, ví dụ trả về đúng nếu $subject
không phải là số.
1
2
3
4
5
6
// Chuoi không có ký tự số
$pattern
=
'/[^0-9]{1,2}/'
;
$subject
=
'sd'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
^
để phủ định một Regex nào đó, ví dụ trả về đúng nếu $subject
không phải là số.
1
2
3
4
5
6
| // Chuoi không có ký tự số $pattern = '/[^0-9]{1,2}/' ; $subject = 'sd' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
Các ký tự Regex đặc biệt
Danh sách các ký tự Regex đặc biệt như sau:
- \d - Chữ số bất kỳ ~ [0-9]
- \D - Ký tự bất kỳ không phải là chữ số (ngược với \d) ~ [^0-9]
- \w - Ký tự từ a-z, A-Z, hoặc 0-9 ~ [a-zA-Z0-9]
- \W - Ngược lại với \w (nghĩa là các ký tự không thuộc các khoảng: a-z, A-Z, hoặc 0-9) ~[^a-zA-Z0-9]
- \s - Khoảng trắng (space)
- \S - Ký tự bất kỳ không phải là khoảng trắng.
Ví dụ: kiểm tra một chuỗi là số hoặc không phải là số
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Kiểm tra là số
$pattern
=
'/\d/'
;
$subject
=
'2'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
// Kiểm tra không phải là số
$pattern
=
'/\D/'
;
$subject
=
'dsd'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi regex so khớp'
;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Kiểm tra là số $pattern = '/\d/' ; $subject = '2' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } // Kiểm tra không phải là số $pattern = '/\D/' ; $subject = 'dsd' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi regex so khớp' ; } |
7. Capturing value trong Regular Expression
Capturing value là gì? để trả lời câu hỏi này tôi sẽ đưa ra một đoạn RegEx để mổ xẻ nó nhé $pattern = '/([a-z]+)([0-9]+)/'
.
Trong đoạn $pattern
trên ta sẽ phân tích thành 3 phần RegEx khác nhau:
- Phần 1: Là toàn bộ pattern, tức là
([a-z]+)([0-9]+)
- Phần 2: Là đoạn con nhỏ pattern đầu tiên
([a-z]+)
- Phần 3: Là đoạn con nhỏ cuối cùng
([0-9]+)
Những phần trên ta gọi là Captering Value Trong Regular Expression.
Ví dụ
1
2
3
4
preg_match(
'/([a-z]+)([0-9]+)/'
,
'freetuts2014'
,
$matches
);
echo
'<pre>'
;
print_r(
$matches
);
echo
'</pre>'
;
Kết quả:
Array
(
[0] => freetuts2014
[1] => freetuts
[2] => 2014
)
Các bạn thấy:
- Phần tử thứ nhất tương ứng với Phần 1.
- Phần tử thứ hai tương ứng với Phần 2
- Phần tử thứ ba tương ứng với Phần 3
Các bạn phải nắm định nghĩa này nhé, nó rất là quan trong khi các bạn kết hớp xử lý với các hàm như preg_replace, preg_match trong php
$pattern = '/([a-z]+)([0-9]+)/'
.$pattern
trên ta sẽ phân tích thành 3 phần RegEx khác nhau:([a-z]+)([0-9]+)
([a-z]+)
([0-9]+)
1
2
3
4
| preg_match( '/([a-z]+)([0-9]+)/' , 'freetuts2014' , $matches ); echo '<pre>' ; print_r( $matches ); echo '</pre>' ; |
8. Greedy trong Regular Expression
Greedy là gì? Cũng như phần Capturing Value tôi sẽ đưa ra một ví dụ cho dễ hiểu nhé.
1
2
3
4
5
// Tìm chuỗi bắt đầu bằng h và kết thúc chữ o
preg_match(
'/h(.+)o/'
,
'hello la xin chao'
,
$matches
);
echo
'<pre>'
;
print_r(
$matches
);
echo
'</pre>'
;
Chạy và kết quả như sau:
Array
(
[0] => hello la xin chao
[1] => ello la xin cha
)
Tại sao kết quả tứ lung tung thế nhỉ. Tại vì nó tìm chữ h
và nó thấy chữ h
ngay chuỗi hello
đầu tiên, tiếp theo là một chuỗi bất kì và kết thúc là chữ o
nên nó duyệt và thấy chữ o
ở ngay chữ chao
nên nó lấy từ đầu đến cuối. Như vậy có cách nào để lấy chứ hello đầu tiên không? Bạn xem code nhé.
1
2
3
4
5
// Tìm chuỗi bắt đầu bằng h và kết thúc chữ o
preg_match(
'/h(.+?)o/'
,
'hello la xin chao'
,
$matches
);
echo
'<pre>'
;
print_r(
$matches
);
echo
'</pre>'
;
Chạy lên kết quả sẽ là:
Array
(
[0] => hello
[1] => ell
)
Đây chính là kết quả ta mong đợi, sự khác biệt giữa 2 cách là ở cách 2 tôi thêm dấu ?
ở kế ngay dấu +
ở chuỗi pattern.
Vậy Greedy trong Regular Expression là tính chất tham ăn, nó lấy hết cho tới khi gặp ký tự cuối cùng. Và ta dùng dấu ?
đặt sau các Regex để khắc phục tình trạng Greedy.
1
2
3
4
5
| // Tìm chuỗi bắt đầu bằng h và kết thúc chữ o preg_match( '/h(.+)o/' , 'hello la xin chao' , $matches ); echo '<pre>' ; print_r( $matches ); echo '</pre>' ; |
h
và nó thấy chữ h
ngay chuỗi hello
đầu tiên, tiếp theo là một chuỗi bất kì và kết thúc là chữ o
nên nó duyệt và thấy chữ o
ở ngay chữ chao
nên nó lấy từ đầu đến cuối. Như vậy có cách nào để lấy chứ hello đầu tiên không? Bạn xem code nhé.
1
2
3
4
5
| // Tìm chuỗi bắt đầu bằng h và kết thúc chữ o preg_match( '/h(.+?)o/' , 'hello la xin chao' , $matches ); echo '<pre>' ; print_r( $matches ); echo '</pre>' ; |
?
ở kế ngay dấu +
ở chuỗi pattern.?
đặt sau các Regex để khắc phục tình trạng Greedy.9. Các ví dụ về Regular Expression
Kiểm tra chuỗi có phải định dạng năm hay không?
1
2
3
4
5
6
7
8
// Pattern đúng nếu là chữ A hoặc chữ B
// Kiểm tra một chuỗi có phải là định dạng năm không,
// Gợi ý: Định dạng năm có 4 ký tự số
$pattern
=
'/^\d{4}$/'
;
$subject
=
'1234'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Năm đúng'
;
}
1
2
3
4
5
6
7
8
| // Pattern đúng nếu là chữ A hoặc chữ B // Kiểm tra một chuỗi có phải là định dạng năm không, // Gợi ý: Định dạng năm có 4 ký tự số $pattern = '/^\d{4}$/' ; $subject = '1234' ; if (preg_match( $pattern , $subject )){ echo 'Năm đúng' ; } |
kiểm tra chuỗi có phải là số hay không
1
2
3
4
5
$pattern
=
'/^[0-9]+$/'
;
$subject
=
'1234'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuoi la số'
;
}
1
2
3
4
5
| $pattern = '/^[0-9]+$/' ; $subject = '1234' ; if (preg_match( $pattern , $subject )){ echo 'Chuoi la số' ; } |
kiểm tra chuỗi phải là dạng ngay/thang/nam hay không
Vú dụ ngày 20/06/2015
,
1
2
3
4
5
$pattern
=
'/^[0-9]{2}/[0-9]{2}/[0-9]{4}$/'
;
$subject
=
'12/10/2014'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Đúng định dạng ngày tháng năm'
;
}
20/06/2015
,
1
2
3
4
5
| $pattern = '/^[0-9]{2}/[0-9]{2}/[0-9]{4}$/' ; $subject = '12/10/2014' ; if (preg_match( $pattern , $subject )){ echo 'Đúng định dạng ngày tháng năm' ; } |
Kiểm tra chỗi là ABC hoặc CDF
1
2
3
4
5
6
// Chuỗi là ABC hoặc là CDF
$pattern
=
'/^(ABC)|(CDF)$/'
;
$subject
=
'ABC'
;
if
(preg_match(
$pattern
,
$subject
)){
echo
'Chuỗi so khớp'
;
}
Ví du này tôi đã dùng kỹ thuật gom nhóm Capturing Value và dùng toáng tử OR.
1
2
3
4
5
6
| // Chuỗi là ABC hoặc là CDF $pattern = '/^(ABC)|(CDF)$/' ; $subject = 'ABC' ; if (preg_match( $pattern , $subject )){ echo 'Chuỗi so khớp' ; } |
Comments
Post a Comment