Hướng dẫn sử dụng biểu thức chính quy trong ECMAScript / Javascript

Xem thêm các chuyên mục:

1- Regular Expression

Một biểu thức chính quy (Regular expressions) định nghĩa một khuôn mẫu (pattern) tìm kiếm chuỗi. Nó có thể được sử dụng tìm kiếm, sửa đổi, và thao tác trên văn bản. Khuôn mẫu được định nghĩa bởi biểu thức chính quy có thể khớp một hoặc một vài lần, hoặc không khớp với một văn bản cho trước.

Viết tắt của biểu thức chính quy là regex
Biểu thức chính quy (Regular expression) được hỗ trợ bởi hầu hết các ngôn ngữ lập trình, ví dụ, Java, C#, C/C++, v..v Thật không may mỗi ngôn ngữ hỗ trợ biểu thức thông thường hơi khác nhau.

Có thể bạn quan tâm:

2- Quy tắc viết biểu thức chính quy

TT Biểu thức chính quy Mô tả
1 . Khớp (match) với bất kỳ ký tự nào
2 ^regex Biểu thức chính quy phải  khớp tại điểm bắt đầu
3 regex$ Biểu thức chính quy phải khớp ở cuối dòng.
4 [abc] Thiết lập định nghĩa, có thể khớp với a hoặc b hoặc c.
5 [abc][vz] Thiết lập định nghĩa, có thể khớp với a hoặc b hoặc c theo sau là v hay z.
6 [^abc] Khi dấu ^ xuất hiện như là ký tự đầu tiên trong dấu ngoặc vuông, nó phủ nhận mô hình. Điều này có thể khớp với bất kỳ ký tự nào ngoại trừ a hoặc b hoặc c.
7 [a-d1-7] Phạm vi: phù hợp với một chuỗi giữa a và điểm d và con số từ 1 đến 7.
8 X|Z Tìm X hoặc Z.
9 XZ Tìm X và theo sau là Z.
10 $ Kiểm tra kết thúc dòng.
 
11 \d Số bất kỳ, viết ngắn gọn cho [0-9]
12 \D Ký tự không phải là số, viết ngắn gon cho [^0-9]
13 \s Ký tự khoảng trắng, viết ngắn gọn cho [ \t\n\x0b\r\f]
14 \S Ký tự không phải khoản trắng, viết ngắn gọn cho [^\s]
15 \w Ký tự chữ, viết ngắn gọn cho [a-zA-Z_0-9]
16 \W Ký tự không phải chữ, viết ngắn gọn cho [^\w]
17 \S+ Một số ký tự không phải khoảng trắng (Một hoặc nhiều)
18 \b Ký tự thuộc a-z hoặc A-Z hoặc 0-9 hoặc _, viết ngắn gọn cho [a-zA-Z0-9_].
 
19 * Xuất hiện 0 hoặc nhiều lần, viết ngắn gọn cho {0,}
20 + Xuất hiện 1 hoặc nhiều lần, viết ngắn gọn cho {1,}
21 ? Xuất hiện 0 hoặc 1 lần, ? viết ngắn gọn cho {0,1}.
22 {X} Xuất hiện X lần, {}
23 {X,Y} Xuất hiện trong khoảng X tới Y lần.
24 *? * có nghĩa là xuất hiện 0 hoặc nhiều lần, thêm ? phía sau nghĩa là tìm kiếm khớp nhỏ nhất.

3- Các ký tự đặc biệt trong ECMAScript Regex

Một số ký tự đặc biệt trong ECMAScript Regex:
\.[{(*+?^$|
 
Những ký tự liệt kê ở trên là các ký tự đặc biệt. Trong ECMAScript Regex bạn muốn nó hiểu các ký tự đó theo cách thông thường bạn cần thêm dấu \ ở phía trước.

Chẳng hạn ký tự chấm . ECMAScript Regex đang hiểu là một ký tự bất kỳ, nếu bạn muốn nó hiểu là một ký tự chấm thông thường, cần phải có dấu \ phía trước.
// Mẫu regex mô tả một ký tự bất kỳ.
let regex1 = /./ ;

// Mẫu regex mô tả  ký tự dấu chấm.
let regex2 = /\./ ;

4- Tạo một RegExp

Có 2 cách để bạn tạo một đối tượng RegExp.
// Create via constructor of RegExp.
var pattern = new RegExp(pattern, attributes);
 
//
var pattern = /pattern/attributes;

Attribute: m (Multiple)

Nếu một chuỗi có chứa các ký tự xuống dòng ( \n ) (New line), bạn có thể dùng ký tự này để phân tách chuỗi ban đầu thành nhiều chuỗi con. Một biểu thức chính quy với thuộc tính m (Multiple) nói với chương trình rằng hãy chia chuỗi ban đầu thành nhiều chuỗi con theo cách nói trên, rồi thực hiện nhiệm vụ hiện tại với từng chuỗi đó.

Attribute: i (Ignore Case)

Thuộc tính i (Ignore Case) nói với chương trình rằng không quan tâm tới chữ hoa hoặc chữ thường trong khi so khớp.

Attribute: g (Global)

Thuộc tính g, viết tắt của từ Global.
Chú ý: Bạn sẽ hiểu hơn về thuộc tính g (Global) trong các ví dụ.

Attribute: u (Unicode)

Attribute: y (Sticky)

5- Các ví dụ cơ bản

aRegex.test(aString) là phương thức cơ bản nhất giúp bạn kiểm tra một chuỗi aString theo quy tắc: Nếu tìm thấy một chuỗi con của chuỗi aString khớp với biểu thức chính quy aRegex nó sẽ trả về true, ngược lại trả về false.
Các ví dụ cơ bản dưới đây giúp bạn hiểu hơn về các quy tắc để thiết lập một biểu thức chính quy.

Example 1:

regex-test-example1.js
// Một biểu thức chính quy với quy tắc:
// Chữ ABC, tiếp theo sau là một ký tự bất kỳ xuất hiện 1 hoặc nhiều lần.
// Quy tắc: ., +
let aRegex = /ABC.+/

let str1 = "AB DE";

// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "123 ABC";
//
let result2 = aRegex.test(str2);

console.log(result2); // false

let str3 = "123 ABCdef";
//
let result3 = aRegex.test(str3);

console.log(result3); // true

Example 2:

regex-test-example2.js
// Một biểu thức chính quy với quy tắc:
// Chữ ABC, tiếp theo sau là x hoặc y hoặc z
// Tiếp theo sau nữa là một ký tự bất kỳ xuất hiện 1 hoặc nhiều lần
// Quy tắc: [abc], . , +
let aRegex = /ABC[xyz].+/

let str1 = "123ABCx";
// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "123ABCzMNP";
//  
let result2 = aRegex.test(str2);

console.log(result2); // true

Example 3:

regex-test-example3.js
// Một biểu thức chính quy với quy tắc:
// Chữ TOM hoặc JERRY
// Tiếp theo là ký tự bất kỳ xuất hiện 1 hoặc nhiều lần
// Tiếp theo sau nữa là chữ CAT
// Quy tắc: (X|Z), . , +
let aRegex = /(TOM|JERRY).+CAT/

let str1 = "I saw TOM, TOM caught JERRY";
// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "I saw TOM, TOM is a CAT";
// 
let result2 = aRegex.test(str2);

console.log(result2); // true


let str3 = "JERRY is a mouse, It is afraid of the CAT";
//  
let result3 = aRegex.test(str3);

console.log(result3); // true

Example 4:

regex-test-example4.js
// Một biểu thức chính quy với quy tắc:
// Bắt đầu với chữ TOM hoặc JERRY
// Tiếp theo là ký tự bất kỳ xuất hiện 1 hoặc nhiều lần
// Tiếp theo sau nữa là chữ CAT, và kết thúc
// Quy tắc: ^, (X|Z), . , +, $
let aRegex = /^(TOM|JERRY).+CAT$/

let str1 = "I saw TOM, TOM caught JERRY";
// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "I saw TOM, TOM is a CAT";
// 
let result2 = aRegex.test(str2);

console.log(result2); // false


let str3 = "JERRY is a mouse, It is afraid of the CAT";
// 
let result3 = aRegex.test(str3);

console.log(result3); // true

Example 5:

regex-test-example5.js
// Một biểu thức chính quy với quy tắc:
// Bắt đầu với một ký tự số, xuất hiện 5 đến 7 lần, và kết thúc.
// Quy tắc: ^, \d, {X,Y}, $
let aRegex = /^\d{5,7}$/

let str1 = "The Result is: 12345";
// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "A12345";
// 
let result2 = aRegex.test(str2);

console.log(result2); // false


let str3 = "912345";
// 
let result3 = aRegex.test(str3);

console.log(result3); // true

Example 6 (Attribute: m)

Ví dụ, một biểu thức chính quy với tham số m (Multiple Line).
regex-test-attr-m-example.js
// Một biểu thức chính quy với quy tắc:
// Bắt đầu với một ký tự số, xuất hiện 5 đến 7 lần, và kết thúc.
// Quy tắc: ^, \d, {X,Y}, $
let aRegex = /^\d{5,7}$/m

let str1 = "The Result is: 12345";
// 
let result1 = aRegex.test(str1);

console.log(result1); // false

// Chuỗi này tách thành 2 chuỗi con:
// 'The Result is:' & '12345'
let str2 = "The Result is:\n12345";
// 
let result2 = aRegex.test(str2);

console.log(result2); // true

Example 7 (Attribute: i)

Ví dụ, một biểu thức chính quy với tham số i (Ignore Case), thuật toán sẽ không quan tâm tới sự khác biệt chữ hoa, chữ thường trong khi so khớp.
regex-test-attr-i-example.js
// Một biểu thức chính quy với quy tắc:
// Chữ TOM hoặc JERRY
// Tiếp theo là ký tự bất kỳ xuất hiện 1 hoặc nhiều lần
// Tiếp theo sau nữa là chữ CAT
// Quy tắc: (X|Z), . , +
let aRegex = /(TOM|JERRY).+CAT/i

let str1 = "I saw TOM, TOM is a cat";
// Tồn tại hay không chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.test(str1);

console.log(result1); // false


let str2 = "I saw TOM, TOM is a CAT";
// 
let result2 = aRegex.test(str2);

console.log(result2); // true

6- Các phương thức liên quan đến RegExp

RegExp.exec(string)

Phương thức này tìm kiếm một chuỗi con phù hợp với biểu thức chính quy, nếu tìm thấy nó trả về một mảng có dạng [substring, index: idx, input: currentString], ngược lại nó trả về null. Vị trí tìm thấy chuỗi con phù hợp là index:idx sẽ được sử dụng như là điểm bắt đầu cho lần tìm tiếp theo nếu biểu thức chính quy có tham số g (Global), ngược lại vị trí bắt đầu tìm kiếm là 0.
Ví dụ, Sử dụng phương thức RegExp.exec(string), trong đó biểu thức chính quy không có tham số g (Global):
regex-exec-example1.js
// Một biểu thức chính quy với quy tắc:
// Chữ ABC, tiếp theo sau là một ký tự số bất kỳ xuất hiện 1 hoặc nhiều lần.
// Quy tắc: ., \d, +
let aRegex = /ABC\d+/

let str1 = "ABC 123";

// Tìm chuỗi con của chuỗi str1 khớp với aRegex?
let result1 = aRegex.exec(str1);

console.log(result1); // null

// 
let str2 = "123 ABC123 ABC45 x";
let result2 = aRegex.exec(str2);

console.log(result2); // [ 'ABC123', index: 4, input: '123 ABC123 ABC45 x' ]

console.log(result2[0]); // ABC123
console.log(result2["index"]); // 4
console.log(result2["input"]); // 123 ABC123 ABC45 x
Ví dụ sử dụng phương thức RegExp.exec(string), biểu thức chính quy có tham số g (Global):
regex-exec-attr-g-example.js
// Một biểu thức chính quy với quy tắc:
// Một ký tự số bất kỳ xuất hiện 1 hoặc nhiều lần.
// Quy tắc: \d, +
let aRegex = /\d+/g

let str1 = "ABC 123 X22 345";

let array1;

do {
  array1 = aRegex.exec(str1);
  console.log(array1);
} while( array1 != null);

String.split()

Phương thức này phân tách (split) chuỗi hiện tại thành các chuỗi con, và trả về một mảng các chuỗi con.
var substrings = str.split([separator[, limit]])          
Các tham số:
  • separator: Một biểu thức chính quy, hoặc một String dùng để phân tách chuỗi hiện tại.
  • limit: Giới hạn số lượng chuỗi con trả về.
string-split-example.js
// Một biểu thức chính quy với quy tắc:
// Ký tự khoảng trắng xuất hiện 1 hoặc nhiều lần
// Quy tắc:  \s, +
let aRegex = /\s+/

let str1 = "Banana \t\t Apple Lemon";

let result1 = str1.split(aRegex);

console.log(result1); // [ 'Banana', 'Apple', 'Lemon' ]

String.search()

Phương thức này trả về chỉ số (index) nơi tìm thấy chuỗi con của chuỗi hiện tại phù hợp với biểu thức chính quy, nếu không tìm thấy nó sẽ trả về -1.
var index = str.search(searchValue)
Các tham số:
  • searchValue: Một biểu thức chính quy hoặc một String để tìm kiếm.
string-replace-example.js
// Một biểu thức chính quy với quy tắc:
// TOM hoặc JERRY
// Quy tắc: (X|Z)
let aRegex = /(TOM|JERRY)/

let str = "I saw TOM, TOM caught JERRY";

let index = str.search(aRegex);

console.log(index); // 6

String.replace(search, newSubString)

Phương thức này tìm kiếm chuỗi con đầu tiên phù hợp với một biểu thức chính quy để thay thế nó bởi một chuỗi con mới. Nếu biểu thức chính quy có tham số g (Global), phương thức sẽ tìm kiếm tất cả các chuỗi con phù hợp với biểu thức chính quy để thay thế bởi chuỗi con mới.
str.replace(regexp|substr, newSubStr|aFunction)
 
Các tham số:
  • regexp: Một biểu thức chính quy.
  • substr: Một chuỗi con.
  • newSubStr: Chuỗi thay thế (The replacement string).
  • aFunction: Một hàm trả về một chuỗi thay thế.
Ví dụ, Sử dụng phương thức String.replace() để thay thế tất cả các khoảng trắng xuất hiện 1 hoặc nhiều lần trong một chuỗi bởi một ký tự dấu trừ ( - ).
string-replace-example.js
// Ký tự khoảng trắng xuất hiện 1 hoặc nhiều lần.
// Quy tắc: \s, +
let aRegex = /\s+/g

let str = "This \t\t is a \n\t text";

str = str.replace(aRegex, '-');

console.log(str); // This-is-a-text

7- Nhóm (Group)

Một biểu thức chính quy bạn có thể tách ra thành các nhóm (group):
// Một biểu thức chính quy
let regex1 = /\w+=\d+/;

// Viết dưới dạng 2 group
let regex2 = /(\w+)=(\d+)/;

// Một cách khác. 3 group
let regex3 = /(\w+)(=)(\d+)/;
Các group có thể lồng nhau, và như vậy cần một quy tắc đánh chỉ số các group.  Toàn bộ pattern được định nghĩa là group số 0. Còn lại được mô tả giống hình minh họa dưới đây:
indexed-group-example.js
let regex1 = /\w+=\d+/;

let regex2 = /(\w+)=(\d+)/;

let regex3 = /(\w+)(=)(\d+)/;

var STR = "abc=100";

let result1 = regex1.exec(STR);
console.log(result1);// [ 'abc=100', index: 0, input: 'abc=100' ]

let result2 = regex2.exec(STR);
console.log(result2);// [ 'abc=100', 'abc', '100', index: 0, input: 'abc=100' ]

let result3 = regex3.exec(STR);
console.log(result3);// [ 'abc=100', 'abc', '=', '100', index: 0, input: 'abc=100' ]

Chú ý: Sử dụng (?:pattern) để thông báo với ECMAScript không xem đây là một group (None-capturing group)

none-capturing-group-example.js
// Date Format: yyyy-MM-dd
let dateRegex = /([0-9]{4})-(?:[0-9]{2})-([0-9]{2})/;


var STR = "2001-11-24";

let result  = dateRegex.exec(STR);
console.log(result);// [ '2001-11-24', '2001', '24', index: 0, input: '2001-11-24' ]

console.log("Date: " + result[0]); // Date: 2001-11-24
console.log("Year: " + result[1]); // Year: 2001
console.log("Day: " + result[2]);  // Day: 24
Tiếp theo là một ví dụ phức tạp hơn, ví dụ này sẽ cho bạn thấy ích lợi của việc sử dụng group trong biểu thức chính quy.
(var|let|const) var hoặc let hoặc const.
\s*  Ký tự khoảng trắng xuất hiện 0 hoặc nhiều lần.
\s+ Ký tự khoảng trắng xuất hiện 1 hoặc nhiều lần.
\w+  Ký tự chữ xuất hiện 1 hoặc nhiều lần.
\d+ Ký tự số xuất hiện một hoặc nhiều lần.
indexed-group-example2.js
var TEXT = " var aa = 100; let b= 130 ; const c= 110 ; ";


var regex = /\s*(var|let|const)\s+(\w+)\s*=\s*(\d+)\s*;/g;


var result;

while (result = regex.exec(TEXT) ) {

    console.log("substring: " + result[0]);
    console.log("keyword: " + result[1]);
    console.log("variable: " + result[2]);
    console.log("value: " + result[3]);
    console.log("------------------------------");
}
 

Xem thêm các chuyên mục: