Hướng dẫn sử dụng Xử lý chuỗi bằng javascript Regular Expression kiểm tra email, kiểm tra chuỗi, kiểm tra ký tự

Phần 1: Lý thuyết

1. RegEx là gì?

RegEx (Regular Expression) là một biểu thức thường được dùng để mô tả một mẫu chuỗi (string pattern).

2. Vậy, mẫu chuỗi là gì?

Mẫu chuỗi, diễn giải thô ra có nghĩa là các chuỗi văn bản tuân thủ theo một quy luật sắp xếp (mẫu) nào đó.

Ví dụ:
- Một số được viết dưới dạng một chuỗi với các chữ số, có thể có chứa tối đa 1 dấu phẩy ở giữa.
- Dữ liệu kiểu ngày tháng: Thường được viết theu mẫu: dd/mm/yyyy: Hai chữ số đầu tiên xác định ngày, tiếp theo là dấu gạch chéo, tiếp đến là 2 chữ số xác định tháng, tiếp theo là dấu gạch chéo và kết thúc bởi bốn chữ số xác định năm.
- Địa chỉ email: xxx@yyy.zz.gìđó: Bao gồm 1 chuỗi văn bản được chia thành 2 phần: Phần user name và phần domain, 2 phần này cách nhau bởi dấu @. Phần domain có luật riêng theo cách đặt tên của domain...
...

3. Khái niệm về biểu thức:

Một biểu thức bao gồm các toán tử và các toán hạng
Toán tử: Các phép toán (VD: cộng, trừ, nhân, chia, lặp, gộp, so sánh…)
Toán hạng: Biến, giá trị được dùng để tính toán.

4. Vậy regex được dùng để làm gì và nó có tác dụng gì?

Trong các kỹ thuật xử lý chuỗi (VD: Tìm kiếm, thay thế...) thông thường, chúng ta cần phải xác định các chuỗi tường minh để tìm kiếm. Các chuỗi này được gọi là từ khoá.

Tuy nhiên, trong một số trường hợp ngoại lệ khác, chúng ta không thể chỉ định rõ chuỗi từ khoá đó, mà chỉ có thể mô tả được quy luật để sinh ra chuỗi từ khoá đó.

VD: Làm thế nào để lấy được toàn bộ địa chỉ email trong một văn bản?
Rõ ràng với câu hỏi này, bằng mắt thừong thì ta có thể lấy được, còn bằng con đường lập trình thông thường theo kiểu tìm kiếm bằng từ khoá là các địa chỉ email thì chúng ta ... bó tay.

Nhưng nếu có regex, chúng ta có thể thiết lập ra quy luật của một địa chỉ email và yêu cầu chương trinh tìm kiếm theo luật đó.

Cú pháp khai báo regex trong JavaScript

Chúng ta có thể khai báo biến chứa regex như sau:

Mã:
var tên_biến= /mẫu_chuỗi/cách_tìm;
Tham số cách_tìm xác định cách thức tìm kiếm mẫu chuỗi, nó có thể chứa các giá trị sau đây:
g: ("global" matching): Lặp lại quá trình tìm kiếm cho đến khi kết thúc chuỗi. Khi sử dụng phương thức replace(), giá trị này sẽ thay thế tất cả các cụm từ tìm thấy thay vì chỉ cụm từ đầu tiên.
i: so sánh không phân biệt hoa thường.
m: so sánh ở chế độ đa dòng.
Có thể phối hợp nhiều cách_tìm. VD: gi. Cũng có thể không cần đưa tham số này vào

Các phương thức có hỗ trợ regEx trong JavaScript:

Giả sử có một biến chuỗi S:
S.search(): Tìm chuỗi phù hợp với biểu thức đã cho và trả về vị trí của chuỗi đó
S.match(): Tìm chuỗi theo mẫu và trả về một mảng các giá trị tìm thấy
S.replace() Thay thế chuỗi bằng chuỗi khác phù hợp với mẫu tìm kiếm.
...
Các bạn có thể tìm hiểu cách sử dụng các hàm này trên mạng, ở đây tôi chỉ sử dụng hàm match để demo cách sử dụng regex. Nên nhớ, hàm match() này trả về một mảng các giá trị phù hợp với mẫu chuỗi cần tìm.

Ví dụ đơn giản đầu tiên về regex:

Đoạn mã dưới đây trả về mảng các chuỗi có chứa chữ thu:



Mã:
<html>
<body>
<script type="text/javascript">
    var str = "Ngoai hien giot mua thu thanh thot roi"; 
    var patt1 = /thu/; // Mẫu chuỗi xác định một chữ thu
     var result = str.match(patt1);
     document.write(result); // In ra chữ thu
</script>
</body>
</html>
Đoạn ví dụ thứ 2 chỉ tìm kiếm các mẫu chuỗi chứa duy nhất chữ t:



HTML Code:
<html>
    <head>
    <script>
    var s="Thu mot ti, thu hai Ti";
//regex: t/g xác định việc tìm kiếm tất cả các chuỗi là chữ t, 
//và tiến hành tìm từ đầu đến cuối chuỗi nhờ tham số cách tìm /g.  
    document.write(s.match(/t/g));    //Kết quả: t,t,t
//regex: t/gi xác định việc tìm kiếm tất cả các chuỗi là chữ t, 
//và tiến hành tìm từ đầu đến cuối chuỗi không phân biệt chữ hoa, chữ thường nhờ tham số cách tìm /gi.  
     document.write(s.match(/t/gi));    //Kết quả: T,t,t,t,T 
    </script>
    </head>    
</html>
Các phép toán tìm kiếm theo vị trí

^: Trả về chuỗi kết quả trong trường hợp chuỗi này nằm ở vị trí đầu của chuỗi gốc
$: Trả về chuỗi kết quả trong trường hợp chuỗi này nằm ở vị trí cuối của chuỗi gốc
\b: Trả về chuỗi kết quả trong trường hợp chuỗi này nằm ở vị trí đầu của một từ trong chuỗi gốc. Nếu cần so sánh ở vị trí cuối từ, hãy đặt biểu thức \b ở vị trí cuối từ.
\B: Trả về chuỗi kết quả trong trường hợp chuỗi này không nằm ở vị trí đầu của một từ trong chuỗi gốc. (tuỳ thuộc vào vị trí đặt \B ở đầu hoặc cuối một từ)
?=: Trả về chuỗi kết quả nếu theo sau chuỗi đó là một chuỗi nào đó được chỉ định trước
?!: Trả về chuỗi kết quả nếu sau chuỗi đó không phải là một chuỗi nào đó được chỉ định trước

Ví dụ:



HTML Code:
<html>
<head>
   <script>
     var s="Thu mot ti, thu hai Ti";
     document.write(s.match(/^Thu/i)); //Kết quả: Thu, vì chữ Thu nằm ở đầu chuỗi s
     document.write(s.match(/t$/gi));  //Kết quả: null, vì ko tìm thấy chữ t nằm ở cuối chuỗi s
     document.write(s.match(/ti$/gi)); //Kết quả: Ti, vì chữ Ti nằm ở vị trí cuối chuỗi s và thoả mãn điều kiện tìm kiếm /gi (tìm toàn cục ko phân biệt chữ hoa, chữ thường)
     document.write ("<BR>");
     document.write(s.match(/\bti/gi));//Kết quả: ti,Ti vì 2 chữ này nằm ở vị trí đầu của 2 từ ti, Ti trong chuỗi s
     document.write ("<BR>");
     document.write(s.match(/\Bai/gi));//Kết quả: ai, vì chữ này nằm ở phía cuối của chữ hai trong chuỗi s
     document.write ("<BR>");
     document.write(s.match(/th(?=u)/gi));//Kết quả: Th,th, vì 2 chữ này có theo sau (?=) là u, là 2 chữ Thu, thu trong chuỗi gốc.
     document.write ("<BR>");
     document.write(s.match(/t(?!h)/gi));//Kết quả: t,t,T, vì 3 chữ này không theo sau (?!) bởi chữ h, tương ứng với t , ti, Ti trong chuỗi gốc.
   </script>
</head>    
</html>
Các lớp ký tự trong regex:

[xyz]: Tìm một ký tự bất kỳ nằm trong tập ký tự giữa cặp dấu ngoặc vuông
[x-z]: Tìm một ký tự bất kỳ nằm trong tập ký tự từ x đến z
[^xyz]: Tìm một ký tự bất kỳ không thuộc tập ký tự giữa cặp dấu ngoặc vuông.
.: Tìm bất kỳ một ký tự nào không phải là ký tự xuống dòng mới (new line) hoặc ký tự kết thúc dòng (line terminator).
\w: Tìm một ký tự dạng a-Z, 0-9 và dấu gạch dưới.
\W: Ngược lại với \w
\d: Tìm một ký tự thuộc tập ký tự từ 0 đến 9
\D: Ngược lại với \d: Tìm một ký tự không nằm trong tập ký tự từ 0 đến 9
\s: Tìm ký tự cách (dấu cách)
\S:Tìm một ký tự không phải là dấu cách

Ví dụ:


HTML Code:
<html>
<body>

<script type="text/javascript">
var str = "1 voi 1 la 2, 2 them 2 la 4";
// trả về mảng chứa các ký tự trong đoạn từ e tới h
document.write(str.match(/[e-h]/gi));//Kết quả: h,e, với h,e nằm trong chữ them.
document.write ("<BR>");
// Trả về mảng các ký tự không thuộc tập các ký tự từ a-m và ký tự u:
document.write(str.match(/[^a-mu ]/gi));//Kết quả: 1,v,o,1,2,,,2,t,2,4
document.write ("<BR>");
// Tìm các chuỗi con bắt đầu bởi chữ oi, tiếp theo sau đó là 1 ký tự thuộc lớp \W:
document.write(str.match(/oi\W/gi));//oi
document.write ("<BR>");
// Tìm các ký tự là các chữ số:
document.write(str.match(/\d/gi));//1,1,2,2,2,4 
document.write ("<BR>");
// Tìm các chuỗi bắt đầu bởi 1 ký tự thuộc lớp \D, sau đó là 1 ký tự không thuộc nhóm từ b-t:
document.write(str.match(/\D[^b-t ]/g));//v, 1,la, 2, 2, 2,la, 4
document.write ("<BR>");
// Tìm các dấu cách:
document.write(str.match(/\s/g));//, , , , , , , , 
</script>

</body>
</html>
Các phép lặp trong regex

RegEx cho phép tìm kiếm lặp bên trong biểu thức:
{x}: Lặp một ký tự hoặc một biểu thức con trước đó x lần
{x,y}: Lặp một ký tự hoặc một biểu thức con trước đó từ x đến y lần
{x,}: Lặp một ký tự hoặc một biểu thức con trước đó >= x lần
?: Lặp một ký tự hoặc một biểu thức con trước đó 0 hoặc 1 lần
*: Lặp một ký tự hoặc một biểu thức con trước đó >=0 lần
+: Lặp một ký tự hoặc một biểu thức con trước đó >=1 lần

Ví dụ:



HTML Code:
<html>
<body>

<script type="text/javascript">
var str = "Ai mua xoi deeeeee";
// Tìm một chuỗi bao gồm các chữ cái d hoặc e (lớp [de], lặp lại 3 lần:
document.write (str.match(/[de]{3}/g));//dee,eee 
// Tìm một chuỗi bao gồm các chữ cái d hoặc e, lặp lại >=3 lần:
document.write (str.match(/[de]{3,}/g));//deeeeee 
// Tìm một chuỗi bao gồm các chữ cái d hoặc e, lặp lại từ 3 đến 5 lần:
document.write (str.match(/[de]{3,5}/g))//deeee
// Tim chu d, sau chu d la 0 hoac 1 chu e 
document.write (str.match(/de?/g));
// Tim chu d, sau chu d la 0 hoac n chu e 
document.write (str.match(/de*/g));
// Tim chu d, sau chu d la 1 hoac n chu e 
document.write (str.match(/de+/g));
//Tim tat ca cac tu bao gom cac chu cai tu a den z
document.write (str.match(/[a-z]*/gi));
//Tim tat ca cac tu bao gom cac chu cai tu a den z, sau tu la mot dau cach:
document.write (str.match(/[a-z]* +/gi));
</script>
</body>
</html>
Gộp nhóm các biểu thức
Chúng ta có thể sử dụng các dấu ngoặc tròn () để gộp nhóm như trong các biểu thức toán học thông thường.

(): Tìm kiếm một nhóm các ký tự bên trong cặp dấu ngoặc và lưu vào chuỗi kết quả.
(?: ): Tìm kiếm chuỗi kết quả không chứa tập ký tự nằm trong cặp dấu ngoặc.
|: Phép toán hoặc, được sử dụng để kết hợp các mệnh đề với nhau vào chung một biểu thức

Ví dụ:



HTML Code:
<html>
<body>

<script type="text/javascript">
var str = "Email: mail@ttt.com, test@ttt.com.vn,  sinhln@hnue.edu.vn";

// Tim tat ca cac dia chi email trong chuoi
document.write (str.match(/([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+/gi));
//Tim tat ca cac so trong chuoi
str=" 12312 and 343 and 54q";
document.write (str.match(/[0-9]+/g));
// hoac
document.write(str.match(/\d+/g));
</script>
</body>
</html>




Phần 2: Một số trường hợp ứng dụng






Qua phần đó, các bạn hẳn đã hiểu được regex là cái gì, và bước đầu có thể đọc hiểu được ý nghĩa của một chuỗi regex. Bài này đi vào tìm hiểu một số ví dụ ứng dụng regex để xử lý các bài toán trong thực tiễn.

I. Tìm kiếm và thay thế
Hẳn các bạn đã biết, phương thức replace() trong Javascript nếu ở chế độ bình thường thì nó sẽ chỉ tìm và thay thế được duy nhất chuỗi đầu tiên được tìm thấy.
VD:

Code:
<script language="javascript">
var str="chuoi 1, chuoi 2, chuoi 3";
document.write (str.replace ("chuoi","cam")); // Ket qua: cam 1, chuoi 2, chuoi 3
</script>

Vậy làm thế nào để thay thế được tất cả các từ "chuoi" thành "cam"?

Ở đây, chúng ta có thể sử dụng các phép tìm kiếm lặp của regex:
HTML Code:
<script language="javascript">
var str="chuoi 1, chuoi 2, chuoi 3";
// Chuỗi cần tìm là 1 regex: /chuoi/gi, xác định tìm kiếm từ chuoi lặp lại cho đến cuối chuỗi gốc và không phân biệt hoa thường
document.write (str.replace (/chuoi/gi,"cam")); // Ket qua: cam 1, cam 2, cam 3 
</script>
II. Hỗ trợ kiểm tra dữ liệu theo mẫu regex

Ở đây các bạn cần làm quen với một phương thức trong lớp RegExp của javascript, đó là phương thức test().

Cú pháp:
biến_regex.test(chuỗi_gốc)

Phương thức này trả về true nếu như chuỗi gốc khớp với giá trị trả về của biến_regex. Ngược lại là false.

Ví dụ:



HTML Code:
<script language="javascript">
// Khai báo 1 chuỗi thông thường
var str="Hello world!";
//Khai báo 1 chuỗi mẫu (biểu thức regex)
var patt=/Hello/g;
var result=patt.test(str);
document.write("Ket qua: " + result); // Tra ve true vi tim thay tu Hello trong chuoi str
//Một mẫu chuỗi khác
patt=/phpvn/g;
result=patt.test(str);
document.write("<br />Ket qua: " + result); // Tra ve false vi khong thay chuoi phpvn trong chuoi str.
</script>
a. Kiểm tra xem chuỗi nhập vào có phải là 1 số hay không?

Bình thường, chúng ta có thể sử dụng các hàm kết hợp như: isNaN, parseInt, parseFloat... để kiểm tra xem đó có phải là một số hay không? Ở đây tôi cung cấp một cách khác dùng regex:

Đoạn mã dưới đây sẽ chỉ kiểm tra xem đây có phải là 1 số nguyên dương hay không:



HTML Code:
<script language="javascript">
function phpvn_isDigit(myString)
{
     var reg = /^\d+$/;
     return (reg.test(myString));
}
document.write (phpvn_isDigit("12345"));
</script>
Đoạn mã dưới đây sẽ kiểm tra xem có phải là đầu vào là 1 số nguyên hay ko (có thể có thêm dấu +/- ở trước):



HTML Code:
function phpvn_isInt(myString)
{
    var reg= /^(\+|-)?\d+$/;
    return (reg.test(fData));
}
Tiếp tục nâng cấp kiểm tra xem chuỗi đầu vào có phải là một số thực hay không.
Một chuỗi được coi là 1 số thực nếu như nó bắt đầu bởi 1 chuỗi số, có 1 dấu chấm ở giữa và kết thúc bởi 1 chuỗi số:



HTML Code:
function phpvn_isFloat(myString)
{
    var reg= /^(\+|-)?((\d+(\.\d+)?)|(\[0].\d+))$/;
    return (reg.test(myString));
}
Nhưng mẫu chuỗi trên sẽ trả về false nếu người dùng gõ 1 chuỗi dạng 123,123,123.45

Nguồn: phpvn.org