1- Biểu
thức chính quy (Regular expression)
1.1- Tổng
quan
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
1.2- Hỗ trợ các ngôn ngữ
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, Perl, Groovy, vv 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.
2- Quy tắc viết biểu thức chính quy
No
|
Regular
Expression
|
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à nhân vậ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 Java Regex
(Special characters)
Một số ký tự đặc biệt trong Java Regex:
\.[{(*+?^$|
Những ký tự liệt kê ở trên là các ký tự đặc
biệt. Trong Java 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 . java 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ỳ.
String regex = ".";
// Mẫu regex mô tả ký tự dấu chấm.
String regex = "\\.";
4- Sử dụng String.matches(String)
•
Class String
..
// Kiểm tra đối tượng toàn bộ String có khớp với regex hay không.
public boolean matches(String regex)
..
Sử dụng method String.matches(String
regex) cho phép bạn kiểm tra toàn bộ String có khớp với regex không. Đây là một
cách thông dụng nhất. Hãy xem các ví dụ:
•
StringMatches.java
package com.madman.tutorial.regex.stringmatches;
public class StringMatches {
public static void main(String[] args) {
String s1 = "a";
System.out.println("s1=" + s1);
// Kiểm tra toàn bộ s1
// Khớp với bất kỳ ký tự nào
// Quy tắc: .
// ==> true
boolean match = s1.matches(".");
System.out.println("-Match . " + match);
s1 = "abc";
System.out.println("s1=" + s1);
// Kiểm tra toàn bộ s1
// Khớp với bất kỳ ký tự nào.
// ==> false (Rõ ràng, chuỗi 3 ký tự sao khớp với 1 ký tự bất kỳ?)
match = s1.matches(".");
System.out.println("-Match . " + match);
// Kiểm tra toàn bộ s1
// Khớp với bất kỳ ký tự nào 0 hoặc nhiều lần
// Kết hợp các quy tắc: . và *
// ==> true
match = s1.matches(".*");
System.out.println("-Match .* " + match);
String s2 = "m";
System.out.println("s2=" + s2);
// Kiểm tra toàn bộ s2
// Bắt đầu bởi m
// Quy tắc ^
// true
match = s2.matches("^m");
System.out.println("-Match ^m " + match);
s2 = "mnp";
System.out.println("s2=" + s2);
// Kiểm tra toàn bộ s2
// Bắt đầu bởi m
// Quy tắc ^
// ==> false (Rõ ràng, chuỗi 3 ký tự sao khớp với 1 ký tự bất kỳ bắt đầu bởi m)
match = s2.matches("^m");
System.out.println("-Match ^m " + match);
// Bắt đầu bởi m
// Sau đó là ký tự bất kỳ, xuất hiện 1 hoặc nhiều lần.
// Quy tắc ^ và . và +
// true
match = s2.matches("^m.+");
System.out.println("-Match ^m.+ " + match);
String s3 = "p";
System.out.println("s3=" + s3);
// Kiểm tra s3 kết thúc bằng p
// Quy tắc $
// true
match = s3.matches("p$");
System.out.println("-Match p$ " + match);
s3 = "2nnp";
System.out.println("s3=" + s3);
// Kiểm tra toàn bộ s3
// Kết thúc bằng p
// ==> false (Rõ ràng, chuỗi 4 ký tự sao khớp với 1 ký tự p cuối cùng)
match = s3.matches("p$");
System.out.println("-Match p$ " + match);
// Kiểm tra toàn bộ s3
// Ký tự bất kỳ xuất hiện 1 lần: .
// tiếp theo là n, xuất hiện 1 hoặc tối đa 3 lần.
// Kết thúc bởi p: p$
// Kết hợp các quy tắc: . , {X,Y}, $
// true
match = s3.matches(".n{1,3}p$");
System.out.println("-Match .n{1,3}p$ " + match);
String s4 = "2ybcd";
System.out.println("s4=" + s4);
// Bắt đầu là 2
// Tiếp theo x hoặc y hoặc z
// Tiếp theo bất kỳ 1 hoặc nhiểu lần.
// Kết hợp các quy tắc: [abc] , . , +
// true
match = s4.matches("2[xyz].+");
System.out.println("-Match 2[xyz].+ " + match);
String s5 = "2bkbv";
// Bắt đầu là bất kỳ, 1 hoặc nhiểu lần
// Tiếp theo a hoặc b, hoặc c: [abc]
// Tiếp theo z hoặc v: [zv]
// Tiếp theo bất kỳ
// true
match = s5.matches(".+[abc][zv].*");
System.out.println("-Match .+[abc][zv].* " + match);
}
}
Kết quả chạy ví dụ:
•
SplitWithRegex.java
package com.madman.tutorial.regex.stringmatches;
public class
SplitWithRegex {
public static
final String TEXT = "This is my text";
public static
void main(String[] args) {
System.out.println("TEXT="
+ TEXT);
//
Khoảng trắng xuất hiện 1 hoặc nhiều lần.
//
Các ký tự khoảng trắng: \t\n\x0b\r\f
//
Kết hợp quy tắc: \s và +
String
regex = "\\s+";
String[]
splitString = TEXT.split(regex);
//
4
System.out.println(splitString.length);
for
(String string : splitString) {
System.out.println(string);
}
//
Thay thế tất cả các khoảng trắng với ký tự tab.
String
newText = TEXT.replaceAll("\\s+", "\t");
System.out.println("New
text=" + newText);
}
}
|
Kết quả chạy ví dụ:
EitherOrCheck.java
package com.madman.tutorial.regex.stringmatches;
public class
EitherOrCheck {
public static
void main(String[] args) {
String
s = "The film Tom and Jerry!";
//
Kiểm tra toàn bộ s
//
Bắt đầu bởi ký tự bất kỳ xuất hiện 0 hoặc nhiều lần
//
Tiếp theo là từ Tom hoặc Jerry
//
Kết thúc bởi ký tự bất kỳ xuất hiện 0 hoặc nhiều lần
//
Kết hợp các quy tắc: ., *, X|Z
//
true
boolean
match = s.matches(".*(Tom|Jerry).*");
System.out.println("s="
+ s);
System.out.println("-Match
.*(Tom|Jerry).* " + match);
s
= "The cat";
//
false
match
= s.matches(".*(Tom|Jerry).*");
System.out.println("s="
+ s);
System.out.println("-Match
.*(Tom|Jerry).* " + match);
s
= "The Tom cat";
//
true
match
= s.matches(".*(Tom|Jerry).*");
System.out.println("s="
+ s);
System.out.println("-Match
.*(Tom|Jerry).* " + match);
}
}
|
Kết quả chạy ví dụ:
5- Sử dụng Pattern và Matcher
1. Pattern là một đối tượng mẫu, một
phiên bản biên dịch của biểu thức chính quy. Nó không có cấu tử public, và
chúng ta sẽ sử dụng method tĩnh compile(String) để tạo đối tượng, với tham số
là biểu thức chính quy.
2. Matcher là một phương tiện để khớp
với String dữ liệu vào với đối tượng Pattern đã tạo trước đó. Class này
không có cấu tử public, và chúng ta lấy đối tượng này thông qua method
matcher(String) của đối tượng pattern. Với tham số String là văn bản cần kiểm
tra.
3. PatternSyntaxException sẽ bị ném ra
nếu biểu thức chính quy có ngữ pháp không chính xác.
String regex= ".xx.";
// Tạo đối tượng Pattern thông qua method
tĩnh.
Pattern pattern = Pattern.compile(regex);
// Lấy ra đối tượng Matcher
Matcher matcher = pattern.matcher("MxxY");
boolean match =
matcher.matches();
System.out.println("Match "+
match);
|
•
Class Pattern:
public static
Pattern compile(String regex, int flags) ;
public static
Pattern compile(String regex);
public Matcher
matcher(CharSequence input);
public static
boolean matches(String regex, CharSequence input);
|
•
Class Matcher:
public int
start()
public int
start(int group)
public int
end()
public int
end(int group)
public String
group()
public String
group(int group)
public String
group(String name)
public int
groupCount()
public boolean
matches()
public boolean
lookingAt()
public boolean
find()
|
Đây là một ví dụ sử dụng Matcher và method
find() để tìm kiếm các chuỗi con khớp với biểu thức chính quy.
•
MatcherFind.java
package com.madman.tutorial.regex;
import
java.util.regex.Matcher;
import
java.util.regex.Pattern;
public class
MatcherFind {
public static
void main(String[] args) {
final
String TEXT = "This \t is a \t\t\t String";
//
Khoảng trắng xuất hiện 1 hoặc nhiều lần.
String
regex = "\\s+";
Pattern
pattern = Pattern.compile(regex);
Matcher
matcher = pattern.matcher(TEXT);
int
i = 0;
while
(matcher.find()) {
System.out.print("start"
+ i + " = " + matcher.start());
System.out.print("
end" + i + " = " + matcher.end());
System.out.println("
group" + i + " = " + matcher.group());
i++;
}
}
}
|
Kết quả chạy ví dụ:
Method Matcher.lookingAt()
•
MatcherLookingAt.java
package com.madman.tutorial.regex;
import
java.util.regex.Matcher;
import
java.util.regex.Pattern;
public class
MatcherLookingAt {
public static
void main(String[] args) {
String
country1 = "iran";
String
country2 = "Iraq";
//
Bắt đầu bởi I tiếp theo là ký tự bất kỳ.
//
Tiếp theo là ký tự a hoặc e.
String
regex = "^I.[ae]";
Pattern
pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher
matcher = pattern.matcher(country1);
//
lookingAt() tìm kiếm khớp phần đầu.
System.out.println("lookingAt
= " + matcher.lookingAt());
//
Trong khi matches() phải khớp toàn bộ
System.out.println("matches
= " + matcher.matches());
//
Reset matcher với text mới, country2
matcher.reset(country2);
System.out.println("lookingAt
= " + matcher.lookingAt());
System.out.println("matches
= " + matcher.matches());
}
}
|
6- 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
String regex = "\\s+=\\d+";
// Viết dưới dạng group, bởi dấu ()
String regex2 = "(\\s+)(=)(\\d+)";
// Một cách khác.
String regex3 = "(\\s+)(=\\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:
Chú ý: Sử dụng (?:pattern) để thông báo với
Java không xem đây là một group (None-capturing group)
Từ Java 7, bạn có thể xác định một group có
tên (?<name>pattern), Và bạn có thể truy cập các nội dung khớp với
Matcher.group (String name). Điều này làm Regex dài hơn, nhưng mã này là có ý
nghĩa hơn, dễ hơn.
Nhóm bắt theo tên cũng có thể được truy cập
thông qua Matcher.group (int group) với các đề án đánh số tương tự.
Nội bộ, Java chỉ lập bản đồ từ tên đến số
nhóm. Do đó, bạn không thể sử dụng cùng tên để bắt 2 nhóm khác nhau.
Hãy xem một ví dụ sử dụng đánh tên cho nhóm
(group) (Java >=7)
•
NamedGroup.java
package com.madman.tutorial.regex;
import
java.util.regex.Matcher;
import
java.util.regex.Pattern;
public class
NamedGroup {
public static
void main(String[] args) {
final
String TEXT = " int a = 100;float b= 130;float c= 110 ; ";
//
Sử dụng (?<groupName>pattern) để định nghĩa một Group có tên: groupName
//
Định nghĩa group có tên declare: sử dụng (?<declare> ...)
//
Và một group có tên value: sử dụng: (?<value> ..)
String
regex = "(?<declare>\\s*(int|float)\\s+[a-z]\\s*)=(?<value>\\s*\\d+\\s*);";
Pattern
pattern = Pattern.compile(regex);
Matcher
matcher = pattern.matcher(TEXT);
while
(matcher.find()) {
String
group = matcher.group();
System.out.println(group);
System.out.println("declare:
" + matcher.group("declare"));
System.out.println("value:
" + matcher.group("value"));
System.out.println("------------------------------");
}
}
}
|
Kết quả chạy ví dụ:
Để dễ hiểu bạn có thể xem hình minh họa dưới đây:
7- Sử dụng Pattern, Matcher, Group và *?
Trong một số tình huống *? rất quan trọng,
hãy xem một ví dụ sau:
// Đây là một regex
// Bắt gặp ký tự bất kỳ 0 hoặc nhiều lần,
// sau đó tới ký tự ' và tiếp theo là >
String regex = ".*'>";
// Đoạn TEXT1 sau đây có vẻ hợp với regex
nói trên.
String TEXT1 = "FILE1'>";
// Đoạn TEXT2 sau cũng hợp với regex nói
trên.
|
*? sẽ tìm ra một phù hợp nhỏ nhất. Chúng ta
xem ví dụ sau:
•
NamedGroup2.java
package com.madman.tutorial.regex;
import
java.util.regex.Matcher;
import
java.util.regex.Pattern;
public class
NamedGroup2 {
public static void
main(String[] args) {
// Java
>= 7.
// Định
nghĩa một group có tên fileName
// *?
==> Nó sẽ tìm một phù hợp nhỏ nhất.
String
regex = "/file/(?<fileName>.*?)'>";
Pattern
pattern = Pattern.compile(regex);
Matcher
matcher = pattern.matcher(TEXT);
while
(matcher.find()) {
System.out.println("File
Name = " + matcher.group("fileName"));
}
}
}
|
Kết quả chạy ví dụ: