Tải bản đầy đủ (.docx) (13 trang)

tiểu luận môn Nguyên lý các ngôn ngữ lập trình. Đề tài tìm hiểu BIỂU THỨC CHÍNH QUY VÀ THƯ VIỆN BIỂU THỨC CHÍNH QUY CỦA CÁC NGÔN NGỮ LẬP TRÌNH PHP, JAVA, .NET

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (211.48 KB, 13 trang )

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
  

BÀI TIỂU LUẬN
BIỂU THỨC CHÍNH QUY VÀ THƯ VIỆN BIỂU THỨC CHÍNH QUY
CỦA CÁC NGÔN NGỮ LẬP TRÌNH PHP, JAVA, .NET
Giáo viên hướng dẫn:

TS. Nguyễn Hữu Đức

Học viên thực hiện :

Đỗ Ngọc Phục

CB120105

Lê Gia Vĩnh

CB120125

Nguyên Hồng Tâm

CB120110

Lớp

:

Công nghệ thông tin 2 (KT)


Đề bài

:

Đề số 1

HÀ NỘI 12 – 2012


Mục lục


I.

Định nghĩa biểu thức chính quy (regular expression defined)

Biểu thức chính quy (regular expression, viết tắt là regexp, regex hay regxp) là
một chuỗi miêu tả một bộ các chuỗi khác, theo những quy tắc cú pháp nhất định.
Biểu thức chính quy thường được dùng trong các trình biên tập văn bản và các tiện
ích tìm kiếm và xử lý văn bản dựa trên các mẫu được quy định. Nhiều ngôn ngữ lập
trình cũng hỗ trợ biểu thức chính quy trong việc xử lý chuỗi, chẳng hạn như
C#, Perl có bộ máy mạnh mẽ để xử lý biểu thức chính quy được xây dựng trực tiếp
trong cú pháp của chúng. Bộ các trình tiện ích (gồm trình biên tập SED và trình
lọc GREP) đi kèm các bản phân phối Unix có vai trò đầu tiên trong việc phổ biến
khái niệm biểu thức chính quy.
Thuật ngữ regular expression xuất phát từ lý thuyết toán học và khoa học máy
tính, nó phản ánh một đặc điểm của các biểu thức toán học được gọi là chính quy
(regularity). Một biểu thức có thể được thực hiện trong một phần mềm bằng cách sử
dụng một bộ xác định giới hạn tự động (Deterministic Finite Automation – DFA).
DFA là một trạng thái xác định và không sử dụng cơ chế quay lui (backtracking).

Nếu sử dụng tốt những kỹ năng về regular expression. Chúng sẽ đơn giản hơn
nhiều trong lập trình và quá trình xử lý văn bản, và có những vấn đề sẽ không thể
giải quyết được nếu không sử dụng regular expression. Sẽ cần đến hàng trăm thủ tục
để trích xuất tất cả các địa chỉ email từ một số tài liệu, đây có thể nói là một việc làm
tẻ nhạt và vất vã. Nhưng với regular expression thì chỉ cần một số dòng lệnh hoặc
thậm chí một dòng lệnh để làm việc này.
Tuy nhiên, Regular expression cũng thường đem đến cho người sử dụng
những phiền toái không mong muốn như: sử dụng một biểu thức chính quy không
phù hợp với biểu thức muốn tìm, hoặc số văn bản tìm được với biểu thức chính quy
đó không phù hợp… Để sử dụng regular expression cần phải có một kiến thức từ cơ
bản đến nâng cao về những biểu thức và cách thức hoạt động của nó trong các ngôn
ngữ lập trình.
Regular expression là một công cụ mạnh mẽ trong việc thao tác và trích xuất
văn bản trên máy tính. Do đó nắm vững các biểu thức chính quy sẽ giúp tiết kiệm
nhiều thời gian và công sức.
Có thể hiểu đơn giản Regex là 1 cái mẫu (pattern) dùng để mô tả 1 lớp ký tự
nào đó.


VD: lazydog là 1 regex. Nó là 1 mẫu đơn giản nhất vì nó so khớp (match) với đoạn
văn bản lazydog. 1 match là 1 đoạn văn bản được so khớp với mẫu.
VD phức tạp hơn 1 chút: \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b Đây là mẫu
mô tả 1 địa chỉ email. Mẫu này có thể được dùng để tìm 1 địa chỉ email trong 1 đoạn
văn bản, hoặc kiểm tra xem 1 chuỗi có phải là địa chỉ email hợp lệ hay không.
Regex có thể được sử dụng với bất kỳ dữ liệu nào mà ta có thể truy cập, thông
qua ứng dụng hoặc ngôn ngữ lập trình. Có thể kể đến 1 số ứng dụng xử lý văn bản
hỗ trợ regex: PowerGREP, EditPad Pro, RegexBuddy,…

II.


Regular Expression Engine

1. Khái niệm
Regex engine là 1 bộ phận của phần mềm, chuyên để xử lý regex (so khớp
mẫu với 1 chuỗi nào đó). Có nhiều regex engine và chúng không hoàn toàn tương
thích với nhau. Cú pháp regex (flavor) của mỗi engine cũng có sự khác nhau. Ở đây
sẽ tập trung vào cú pháp regex được sử dụng trong Perl 5, vì nó phổ biến nhất. Rất
nhiều engine regex khác giống với engine sử dụng trong Perl 5: engine nguồn mở
PCRE (sử dụng trong rất nhiều ngôn ngữ lập trình, như PHP), thư viện regex .NET,

2. Ký tự thông thường và ký tự đặc biệt
a. Ký tự thông thường (Literal Characters)

Regex cơ bản nhất chính là biểu thức bao gồm 1 ký tự thông thường, VD: a.
Nó sẽ so khớp với thực thể đầu tiên của ký tự đó trong chuỗi. VD nếu có
chuỗi: LazyDog is a boy, nó sẽ so khớp với ký tự a sau ký tự L. Regex này cũng có
thể so khớp với ký tự a thứ 2 nếu ta điều khiển regex engine tiếp tục tìm kiếm sau
khi đã so khớp được 1 lần.
Cũng như vậy, regex dog sẽ so khớp với dog trong chuỗi LazyDog is not a
dog. Regex này bao gồm 1 chuỗi 3 ký tự thông thường. Engine sẽ hiểu biểu thức này
là: tìm d, theo sau bởi o, theo sau bởi g.
Chú ý rằng regex engine mặc định phân biệt chữ hoa và chữ
thường. Dog không so khớp với dog.


b. Ký tự đặc biệt (Special Characters)

Vì ta cần làm nhiều công việc phức tạp hơn là tìm kiếm 1 đoạn văn bản, cho
nên phải trưng dụng 1 vài ký tự để làm những nhiệm vụ đặc biệt. Trong cú pháp
regex, có 11 ký tự mang ý nghĩa đặc biệt: [ \ ^ $ . | ? * + ( ). Chúng được gọi là các

metacharacter.
Nếu cần dùng các ký tự này với ý nghĩa thông thường, ta phải giải phóng nó
bằng ký tự \. VD nếu cần so khớp 1+1=2, thì regex đúng sẽ là 1\+1=2. Chú ý
rằng 1+1=2 cũng là regex đúng, nên sẽ không báo lỗi, nhưng nó sẽ không cho ta kết
quả như mong muốn. Regex 1+1=2 sẽ so khớp với 111=2 trong chuỗi 123+111=234,
vì dấu + ở đây mang ý nghĩa đặc biệt.
Nếu ta quên không giải phóng ký tự đặc biệt ở những chỗ nó không được phép
đứng thì sẽ gặp lỗi. VD: +1
Hầu hết các loại cú pháp regex đều coi { như 1 ký tự thông thường, trừ khi nó
là 1 phần của toán tử nhắc lại (repetition operator), VD: {1, 3}. Vì vậy ta không cần
giải phóng ký tự này.
Ta chỉ dùng \ để giải phóng các ký tự đặc biệt, còn các ký tự khác thì không
nên, vì \ cũng là 1 ký tự đặc biệt. \ khi kết hợp với 1 ký tự thông thường sẽ có ý
nghĩa đặc biệt, VD: \d sẽ so khớp với 1 chữ số từ 0 - 9.
Tất cả các loại cú pháp regex đều cho phép giải phóng 1 ký tự đặc biệt bằng \.
Rất nhiều cú pháp khác còn hỗ trợ kiểu giải phóng \Q... \E. Tất cả các ký tự nằm
trong cặp \Q và \E sẽ được coi như ký tự thông thường. VD: \Q*\d+*\E sẽ so khớp
với đoạn văn bản *\d+* . Kiểu cú pháp này được hỗ trợ bởi JGsoft engine, Perl,
PCRE, ...
c. Ký tự đặc biệt và ngôn ngữ lập trình

Khác với trong ngôn ngữ lập trình, trong regex, ký tự ' và " không phải là ký
tự đặc biệt. Vì vậy, không cần phải giải phóng nó.
Trong mã nguồn của 1 chương trình, cần luôn ghi nhớ những ký tự nào được
ngôn ngữ lập trình xử lý đặc biệt. Bởi vì những ký tự này sẽ được trình biên dịch xử
lý trước khi được engine regex xử lý. VD: regex 1\+1=2 phải được viết thành
1\\+1=2 trong mã nguồn C++. Trình biên dịch C++ sẽ chuyển \\ thành \ trong chuỗi
trên, sau đó nó mới được chuyển đến regex engine. VD khác: đế so khớp c:\temp,
cần dùng regex c:\\temp (vì \t trong regex mang ý nghĩa đặc biệt). Và trong mã
nguồn C++, regex này cần được viết là c:\\\\temp.



d. Ký tự không in được

Có thể dùng các tổ hợp ký tự đặc biệt để đặt các ký tự không in được vào
regex.
\t cho ký tự tab (ASCII 0x09)
\r cho carriage return (0x0D)
\n cho line feed (0x0A).
\a (bell, 0x07)
\e (escape, 0x1B)
\f (form feed, 0x0C)
\v (vertical tab, 0x0B).
Trong tệp văn bản trong Windows sử dụng \r\n để kết thúc dòng, còn UNIX
sử dụng \n. Có thể dùng cách này để viết bất kỳ ký tự nào nếu biết mã 16 ASCII của
ký tự đó trong bảng mã đang dùng. VD: trong bảng mã Latin-1, ký tự copyright có
mã 0xA9. Vì thế để tìm ký tự này, ta dùng \xA9.
Hầu hết các loại cú pháp regex còn cho phép sử dụng tổ hợp \cA đến \cZ (c cố
định, theo sau bởi 1 chữ cái hoa từ A - Z) để biểu thị ký tự điều khiển. VD: \cA biểu
thị Control+A. \cM biểu thị Control+M, hay carriage return, giống như \r.
Nếu regex engine hỗ trợ Unicode, ta sử dụng \uFFFF thay cho \xFF để biểu thị
1 ký tự Unicode. VD: mã unicode của ký tự đồng euro là 0x20AC. Để đặt nó vào
biểu thức regex, ta dùng \u20AC.
3. Cách thực hiện của Regex Engine
Hiểu được cách làm việc của regex engine sẽ giúp ta viết regex tốt hơn, dễ
dàng hơn. Nó giúp ta hiểu được tại sao 1 regex hoạt động không như mong muốn, và
giúp tiết kiệm thời gian phải mò mẫm khi viết các regex phức tạp.
Có 2 loại regex engine: text-directed engines, và regex-directed engines. Loại
cú pháp regex mà ta đang thảo luận ở đây thuộc loại regex-directed engines. Loại
engine này phổ biến hơn bởi nó có 1 số chức năng rất hữu dụng như: lazy

quantifiers, backreferences,...
Có thể dễ dàng kiểm tra xem loại cú pháp đang sử dụng thuộc về engine nào
qua việc kiểm tra xem lazy quantifiers và backreferences có được hỗ trợ không. Hãy
thử dùng biểu thức regex regex|regex not vào chuỗi regex not. Nếu kết quả so khớp
là regex, thì engine đang dùng thuộc loại regex-directed. Nêu kết quả là regex not,
thì engine thuộc loại text-directed.


Trong các VD ở các bài tiếp theo, ta sẽ phân tích cụ thể cách thức làm việc
của regex engine, qua đó giúp sử dụng regex hiệu quả nhất và tránh mắc lỗi.
Regex-directed engine luôn trả về kết quả so khớp bên trái nhất, thậm chí nếu
1 match tốt hơn có thể được tìm thấy nếu tiếp tục so khớp. Đây là điều cần ghi nhớ.
Regex-directed engine luôn bắt đầu so khớp với ký tự đầu tiên của chuỗi.
Hãy lấy 1 VD đơn giản nhất để minh hoạ: ta dùng regex cat vào chuỗi “He
captured a catfish for his cat”. Engine sẽ bắt đầu so khớp dấu hiện đầu tiên trong
regex là c với ký tự đầu tiên của chuỗi là H. Không khớp, vì vậy nó tiếp tục lần lượt
so khớp với ký tự thứ 2 và 3 là e và space. Đều không khớp, đến ký tự thứ 4, c đã
khớp với c. Xong, giờ engine bắt đầu so khớp dấu hiệu thứ 2 trong regex là a với ký
tự thứ 5 của chuỗi là a, khớp, nhưng đến dấu hiệu thứ 3 của regex là t thì không
khớp với ký tự thứ 6 của chuỗi là p. Lúc này engine nhận ra rằng không thể tìm ra 1
match bắt đầu từ ký tự thứ 4 của chuỗi. Vì vậy, nó bắt đầu lại công việc từ đầu, từ ký
tự thứ 5 của chuỗi, regex c không khớp với a. Cứ tiếp tục như vậy cho đến ký tự thứ
15 của chuỗi, regex c đã khớp với c. Engine lần lượt so khớp các dấu hiệu còn lại
trong regex với các ký tự tiếp theo trong chuỗi: a khớp a, t khớp t. Và như vậy 1
match đã được tìm thấy bắt đầu từ ký tự 15. Engine sẽ trả về kết quả và ngừng luôn,
không tiếp tục tìm xem còn match nào tốt hơn không (VD: cat ở cuối chuỗi).
4. Lớp ký tự (Character Classes - Character Sets)
a. Lớp ký tự

Sử dụng lớp ký tự, ta sẽ khiến regex engine chỉ chọn ra 1 ký tự để so khớp. Để

sử dụng, ta đặt các ký tự cần so khớp vào 2 dấu [ và ]. VD: để so khớp ký tự a hoặc
e, ta dùng [ae]. Như vậy biểu thức gr[ae]y sẽ khớp với gray hoặc grey.
Lớp ký tự chỉ so khớp với 1 ký tự đơn. Như vậy gr[ae]y sẽ không khớp
với graay, graey,v.v… Thứ tự các ký tự trong lớp không quan trọng. Kết quả trả về
luôn giống nhau.
Để xác định 1 vùng ký tự trong lớp ký tự, ta sử dụng dấu - . VD: [0-9] so khớp
với 1 chữ số từ 0 – 9. Có thể sử dụng nhiều vùng ký tự hoặc kết hợp vùng ký tự với
ký tự đơn. VD: [0-9a-fA-F] so khớp với 1 chữ số hệ 16, không phân biệt chữ hoa,
thường. [0-9a-fxA-FX] so khớp với 1 chữ số hệ 16 hoặc chữ cái X, không phân biệt
chữ hoa, thường. Cũng như trên, thứ tự các vùng không quan trọng.


b. Lớp ký tự phủ định

Đặt dấu ^ sau [ trong lớp ký tự sẽ phủ định lớp ký tự đó. Kết quả là lớp ký tự
sẽ so khớp với bất kỳ ký tự nào không nằm trong lớp ký tự đó. Lớp ký tự phủ định
có thể so khớp với cả ký tự line break.
Chú ý rằng lớp ký tự phủ định vẫn phải được so khớp với 1 ký tự.
VD: q[^u] không phải là “q không theo sau bởi u” mà là “q theo sau bởi 1 ký tự
không phải u”. Vì vậy nó sẽ không so khớp với q trong chuỗi Iraq, và sẽ so khớp
với q và space trong chuỗi Iraq is a country.
c. Metacharacter trong lớp ký tự

Trong lớp ký tự, các ký tự mang ý nghĩa đặc biệt hay metacharacter chỉ bao
gồm: ] \ ^ -. Các metacharacter nói ở phần trước khi đặt trong lớp ký tự sẽ chỉ được
coi như ký tự thông thường, và do đó không cần phải giải phóng. VD: để tìm ký tự *
hoặc +, ta dùng [+*].
Để đặt ký tự \ vào trong lớp ký tự với nghĩa thông thường, cần giải phóng nó
bằng 1 ký tự \ khác. VD: [\\x] sẽ khớp với ký tự \ hoặc x. Các ký tự ] ^ - nếu muốn
dùng theo nghĩa thông thường cũng phải được giải phóng bằng \ hoặc đặt nó ở vị trí

mà nó sẽ không có ý nghĩa đặc biệt. Ta nên dùng cách thứ 2 để biểu thức regex trông
dễ nhìn hơn như sau:


Với ^, đặt nó ở bất kỳ chỗ nào trừ vị trí ngay sau [ . VD: [x^] sẽ khớp
với x hoặc ^.



Với ], đặt nó ngay sau [ hoặc [^ . VD: []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.



Với -, đặt nó ngay sau [ hoặc [^ , hoặc ngay trước ]. VD: cả [-x] và [x-] đều
so khớp với - hoặc x.

Có thể sử dụng tất cả các ký tự không in được trong lớp ký tự giống như dùng
chúng ngoài lớp ký tự. VD: [$\u20AC] sẽ khớp với $ hoặc ký tự đồng euro (với giả
định cú pháp regex đang dùng hỗ trợ unicode).
JGsoft engine, Perl và PCRE còn hỗ trợ kiểu \Q…\E trong lớp ký tự để giải
phóng 1 chuỗi ký tự. VD: [\Q[-]\E] sẽ khớp với [ hoặc - hoặc ].
Cú pháp regex của POSIX lại xử lý \ trong lớp ký tự như 1 ký tự thông
thường. Đồng nghĩa với việc ta không thể dùng \ để giải phóng ] ^ -. Để làm việc này


ta chỉ còn cách đặt chúng vào các vị trí như trình bày ở trên. Ngoài ra điều này cũng
đồng nghĩa với việc các cú pháp tắt (shorthand, VD: \d) không còn hiệu lực.
d. Lớp ký tự viết tắt (Shorthand Character Classes)


\d là dạng tắt của [0-9].
\w được gọi là “ký tự từ” (word character). Chính xác những ký tự nào được
khớp với nó thay đổi tuỳ theo mỗi loại cú pháp regex. Trong tất cả các loại cú pháp,
nó sẽ bao gồm [A-Za-z]. Trong hầu hết các loại cú pháp, nó cũng bao gồm cả dấu _
và chữ số.
\s được gọi là “ký tự trắng” (whitespace character). Nó khớp với ký tự nào thì
cũng tùy thuộc vào từng loại cú pháp. Trong kiểu cú pháp thảo luận ở đây, nó bao
gồm [\t]. Nghĩa là \s sẽ khớp với space hoặc tab. Trong hầu hết cú pháp , nó cũng
bao gồm cả ký tự carriage return hoặc line feed, nghĩa là [\t\r\n]. Một số cú pháp
khác lại bao gồm thêm cả các ký tự không in được hiếm khi dùng như vertical tab
hoặc form feed.
Các lớp ký tự viết tắt có thể được dùng cả trong lẫn ngoài cặp [].
VD: \s\d khớp với 1 ký tự trắng theo sau bởi 1 chữ số. [\s\d] khớp với 1 ký tự đơn là
1 ký tự trắng hoặc 1 chữ số. Khi áp dụng vào chuỗi 1 + 2 = 3, regex thứ 1 sẽ khớp
với 2 (space và 2), trong khi regex thứ 2 sẽ khớp với 1. [\da-fA-F] khớp với 1 chữ số
hệ 16, giống như [0-9a-fA-F].
e. Lớp ký tự viết tắt phủ định (Negated Shorthand Character Classes)

\D tương đương [^\d]
\W tương đương [^\w]
\S tương đương [^\s]
Cần thận trọng khi sử dụng dạng viết tắt phủ địng bên trong []. [\D\S] khác
với [^\d\s]. Regex thứ 2 sẽ khớp với bất kỳ ký tự nào không phải là chữ số hoặc ký
tự trắng. Còn regex thứ 1 sẽ khớp với bất kỳ ký tự nào không phải là chữ số hoặc
không phải là ký tự trắng. Và vì chữ số không phải là ký tự trắng và ký tự trắng
không phải là chữ số cho nên [\D\S] sẽ khớp với bất kỳ ký tự nào, bao gồm cả ký tự
trắng và chữ số.
f. Nhắc lại lớp ký tự (Repeating Character Classes)

Nếu nhắc lại lớp ký tự khi dùng các toán tử nhắc lại ? * + , ta sẽ nhắc lại cả

lớp ký tự chứ không chỉ nhắc lại ký tự mà nó so khớp. VD: regex [0-9]+ sẽ khớp với
cả 837 lẫn 222.


Nếu muốn nhắc lại chỉ các ký tự được so khớp, ta cần dùng tham chiếu ngược
(backreferences). ([0-9])\1+ sẽ khớp với 222 chứ không khớp với 837. Khi áp dùng
regex này vào chuỗi 833337, nó sẽ khớp với 3333.
g. Ký tự chấm (Dot)

Ký tự Dot khớp với hầu hết các ký tự. Trong biểu thức regex, dấu . là
metacharacter được sử dụng nhiều nhất, và cũng là ký tự bị sử dụng sai nhiều nhất.
Dấu . khớp với 1 ký tự đơn bất kỳ ngoại trừ ký tự newline. Vì vậy, dấu . tương
đương với [^\n] (trong UNIX) hoặc [^\r\n] (trong Windows).
Trong Perl, dấu . có thể khớp với cả newline nếu ta dùng chế độ “single-line
mode”. Để sử dụng chế độ này, ta thêm s vào sau biểu thức regex, VD: m/^regex$/s;
JavaScript và VBScript không có chế độ nào hỗ trợ Dot so khớp với các ký tự
line break. Vì vậy, để so khớp với bất kỳ ký tự nào ta phải dùng [\s\S] thay cho
Dot. [\s\S] so khớp với 1 ký tự là ký tự trắng (bao gồm cả các ký tự line break) hoặc
không phải ký tự trắng, nghĩa là nó so khớp với bất kỳ ký tự nào.
Dấu . là 1 metacharacter đầy uy lực. Nó có thể khớp với bất kỳ ký tự nào,
nhưng cũng có thể khớp với ký tự mà ta không muốn. Những trường hợp như thế có
thể rất khó nhận ra.
Hãy lấy 1 VD đơn giản để minh hoạ: giả sử ta muốn tìm 1 chuỗi ngày tháng
năm dưới dạng mm/dd/yy, trong đó dấu phân cách ngày tháng năm ta để người dùng
tuỳ chọn. Giải pháp nhanh nhất là \d\d.\d\d.\d\d, trông có vẻ ổn. Nó sẽ khớp 1 chuỗi
kiểu như 02/12/03. Vấn đề là 1 chuỗi kiểu như 02512703 cũng được coi là 1 ngày
hợp lệ với regex trên (chấm thứ 1 khớp với 5, chấm thứ 2 khớp với 7).
Giải pháp tốt hơn là: \d\d[- /.]\d\d[- /.]\d\d. Regex này cho phép – hoặc space
hoặc . hoặc / làm dấu phân cách ngày tháng năm. Lưu ý rằng dấu . trong lớp ký tự là
1 ký tự thông thường, do đó không cần phải giải phóng. Nhưng regex này vẫn chưa

hoàn hảo, nó sẽ coi 99/99/99 là 1 ngày hợp lệ.
Giải pháp tiếp theo: [0-1]\d[- /.][0-3]\d[- /.]\d\d. Ổn hơn nhưng vẫn chưa hoàn
hảo, nó có thể khớp với 19/39/99.
Chất lượng của regex thế nào tuỳ thuộc vào yêu cầu của bạn. VD nếu muốn
thẩm định thông tin nhập vào từ người dùng thì regex phải thật hoàn hảo. Còn nếu
phân tích 1 file dữ liệu mà bạn đã biết chắc mã nguồn tạo ra file đó theo cách thức
như thế nào thì regex có thể ở mức vừa đủ mà thôi.


Sử dụng lớp ký tự phủ định thay cho Dot: Giả sử ta cần tìm 1 chuỗi bao bởi ”
“. Chuỗi này có thể có bao nhiêu ký tự tuỳ thích, do đó “.*” có vẻ ổn. Dấu . khớp với
bất kỳ ký tự nào, còn dấu * sẽ cho phép . có thể được nhắc lại bao nhiêu lần tuỳ
thích, kể cả 0 lần. Nếu áp dụng regex này vào chuỗi Put a “string” between double
quotes, nó sẽ trả về kết quả đúng như mong đợi: “string”. Giờ hãy thử với
chuỗi Houston, we have a problem with “string one” and “string two”. Please
respond. Và kết quả là “string one” and “string two”, đã sai. Lý do là vì * vốn có bản
tính “tham lam” (greedy). VD này cho thấy không nên lạm dụng dấu chấm.
Trong VD tìm ngày ở trên, ta cải tiến regex bằng cách sử dụng lớp ký tự thay
cho dấu chấm. Giờ ta cũng làm như vậy. Ta không muốn có số lượng bất kỳ các ký
tự bất kỳ trong cặp dấu ” “, mà muốn có số lượng bất kỳ các ký tự không phải là ”
hoặc newline trong cặp dấu ” “. Do đó regex đúng sẽ là“[^"\r\n]*”.
h. Mỏ neo (anchors)

Neo đầu và cuối chuỗi, từ đầu đến giờ, ta đã tìm hiểu về các ký tự thông
thường và lớp ký tự. Đặt 1 ký tự thông thường hoặc 1 lớp ký tự vào biểu thức regex,
ta sẽ bắt regex engine đi so khớp với 1 ký tự đơn.
Mỏ neo thuộc về 1 dạng khác. Chúng không so khớp với bất kỳ 1 ký tự nào.
Thay vào đó, chúng so khớp với 1 vị trí trước, sau hoặc giữa các ký tự. Chúng được
sử dụng để “neo” biểu thức regex vào 1 vị trí để so khớp. Dấu ^ khớp với vị trí ngay
trước ký tự đầu tiên trong chuỗi. Áp dụng regex ^a cho chuỗi abc, ta sẽ được a. ^b sẽ

không có kết quả khi so khớp với abc, vì b không khớp với ký tự ngay sau vị trí bắt
đầu của chuỗi, vị trí được khớp bởi ^.
Tương tự như trên, $ khớp với vị trí ngay sau ký tự cuối cùng của chuỗi. c$ sẽ
khớp với c trong abc, trong khi a$ không khớp.

III. Biểu thức chính quy trong các ngôn ngữ lập trình
1. Ngôn ngữ PHP
Trong PHP, ta có thể sử dụng biểu thức regex thông qua các hàm regex. PHP
cung cấp 3 nhóm hàm regex, tên của chúng được bắt đầu bởi: ereg, mb_ereg và preg.
2 loại đầu sử dụng engine POSIX Extended, còn preg sử dụng engine PCRE (PerlCompatible). Xét đoạn mã PHP sau:
$string = ‘chuỗi cần áp dụng biểu thức regex‘;
$pattern = ‘/biểu thức regex/’;


preg_match($pattern, $string, $match);
echo $match[0];
?>
Nó sẽ hiện lên màn hình kết quả so khớp biểu thức regex với chuỗi mà đã áp
dụng regex vào. Như thấy ở trên, ta dùng nhóm hàm preg vì nó sử dụng engine
PCRE rất giống với engine của Perl.
2. Ngôn ngữ C# .NET
Biểu thức chính quy xuất phát từ môi trường UNIX, đuợc dùng với PERL.
Microsoft cho nó vào Windows và cho đến hiện tại thì nó hầu như được sử dụng với
những ngôn ngữ kịch bản. Biểu thức chính quy được hổ trợ bởi một số lớp .NET
trong namespace : System.Text.RegularExpressions
Biểu thức chính quy của .NET framework kết hợp các tính năng phổ biến nhất
của biểu thức chính quy khác thực hiện được như trong Perl và awk. Được thiết kế
để tương thích với biểu thức chính quy trong Perl 5 biểu thức chính quy trong .NET
Framework bao gồm các tính năng chưa từng thấy trong các hệ thống xử lý chính

quy khác, như so khớp phải sang trái và biên soạn kiểu on-the-fly. . NET Framework
thể hiện các lớp như là một phần của thư viện lớp cơ sở và có thể được sử dụng với
bất kỳ ngôn ngữ nào hay các công cụ mà các mục tiêu thực hiện runtime, bao gồm cả
ASP.NET và Visual Studio 2005. Lớp biểu thức chính quy trong .NET Framework
là một phần của thư viện lớp cơ sở và có thể được sử dụng với bất kỳ ngôn ngữ hoặc
công cụ có chỉ dẫn biên dich CLR, bao gồm cả ASP.NET.
Những lớp .NET RegularExpressions hỗ trợ groups và captures, là những lớp
Group và Capture. Cũng có những lớp GroupCollection và CaptureCollection , mà
trình bày việc thu thập groups và captures. Lớp Match thể hiện 1 phương thức
Group() và trả về 1 đối tượng GroupCollection. Lớp Group thi hành 1 phương thức
Captures() và trả về 1 CaptureCollection. Mối quan hệ giữa những đối tượng được
thể hiện qua biểu đồ sau:


việc trả về 1 đối tượng Group mỗi lần ta muốn nhóm 1 số kí tự cùng với nhau có thể
không phải là những gì ta muốn làm. Có 1 số overhead liên quan đến việc khởi tạo
đối tượng, mà bị lãng phí nếu tất cả những gì ta muốn là nhóm một vài kí tự cùng
nhau như là 1 phần pattern .ta có thể không cho phép điều này bằng việc bắt đầu
nhóm với chuỗi kí tự ?: cho mỗi nhóm riêng hoặc cho tất cả những nhóm bằng việc
chỉ định cờ RegExOptions.ExplicitCaptures trên phương thức REgEX.Matches()
3. Ngôn ngữ Java
Năng lực về biểu thức chính quy của ngôn ngữ Java gồm có ba lớp cốt lõi sẽ
sử dụng hầu như mọi lúc:
Pattern, trong đó mô tả một mẫu chuỗi ký tự.
• Matcher, để kiểm tra một chuỗi ký tự xem nó có khớp với mẫu không.
• PatternSyntaxException, để báo cho bạn rằng một số thứ không thể chấp
nhận được với mẫu mà bạn đã thử định nghĩa.
Cách tốt nhất để tìm hiểu về biểu thức chính quy là qua các ví dụ, do đó trong phần
này chúng ta sẽ tạo ra một ví dụ đơn giản trong CommunityApplication.main(). Tuy
nhiên, trước khi chúng ta tiến hành, điều quan trọng là hiểu được một số cú pháp

mẫu biểu thức chính quy . Chúng ta sẽ thảo luận điều đó chi tiết hơn trong phần kế
tiếp.




×