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

Biếu thức chính quy

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 (131.2 KB, 15 trang )

Biểu thức chính qui
Khái niệm về biểu thức chính qui
Biểu thức chính qui là một khuôn mẫu - một tiêu bản - để đợc sánh với một xâu.
Việc sánh một biểu thức chính qui với một xâu thì hoặc thành công hoặc thất bại. Đôi
khi, sự thành công hay thất bại này có thể là tất cả những gì bạn quan tâm tới. Vào
lúc khác, bạn sẽ muốn lấy một khuôn mẫu đã sánh đúng và thay thế nó bằng một xâu
khác, một phần trong đó có thể phụ thuộc đích xác vào cách thức và nơi chốn mà biểu
thức chính qui đợc sánh đúng.
Biểu thức chính qui thờng đợc nhiều chơng trình UNIX dùng tới, nh grep, sed,
awk, ed, vi, emacs và thậm chí cả nhiều vỏ nữa. mỗi chơng trình đều có một tập các
kí tự tiêu bản khác nhau (phần lớn là chờm lên nhau). Perl là một siêu tệp ngữ nghĩa
cho tất cả những công cụ này - bất kì biểu thức chính qui nào mà có thể đợc viết trong
một trong những công cụ UNIX này thì cũng đều có thể đợc viết trong Perl, nhng
không nhất thiết dùng hệt các kí tự đó.
Cách dùng đơn giản về biểu thức chính qui
Nếu chúng ta tìm tất cả các dòng của một tệp có chứa xâu abc, thì ta có thể dùng
chỉ lệnh grep:
grep abc sonefile > result
Trong trờng hợp này, abc là biểu thức chính qui mà chỉ lệnh grep lấy để kiểm tra
cho từng dòng đa vào. Những dòng sán hđúng sẽ đợc chuyển ra lối ra chuẩn (ở đây,
kết thúc với tệp result vì việc chuyển hớng của vỏ).
Trong Perl, ta có thể nói về xâu abc nh biểu thức chính qui bằng việc bao xâu này
trong hai dấu sổ chéo:
if (/abc/) {
print $_;
}
Nhng cái gì đợc kiểm tra so với biểu thức chính qui abc trong trờng hợp này? Tại
sao, anh bạn cũ của chúng ta, biến $_ lại có mặt ở đây? Khi một biểu thức chính qui
đợc bao trong hai dấu sổ chéo (nh trên), thì biến $_ sẽ đợc kiểm tra theo biểu thức
chính qui đó. Nếu biểu thức chính qui sánh đúng, thì toán tử sánh sẽ cho lại giá trị
đúng. Ngoài ra, nó cho lại giá trị sai.


Trong thí dụ này, biến $_ đợc giả sử có chứa một dòng văn bản nào đó, và đợc in
ra nếu dòng này có chứa các kí tự abc đâu đó bên trong dòng - tơng tự nh chỉ lệnh
grep ở trên. Không giống nh chỉ lệnh grep, vốn vận hành trên tất cả các dòng của tệp,
đoạn chơng trình Perl này chỉ nhìn vào có một dòng thôi. Để làm việc trên tất cả các
dòng, ta cần thêm vào một chu trình, nh trong:
while (<>) {
if (/abc/) {
print $_;
}
}
Điều gì sẽ xảy ra nêu nh ta không biết đợc số của các b giữa a và c? Tức là, điều
gì sẽ xảy ra nếu ta muốn in dòng có chứa một a và theo sau nó là không hay nhiều b,
rồi theo sau nữa là một c? Với grep, ta phải nói:
grep ab*c somefile > result
(Đối này có chứa dấu sao trong ngoặc kép bởi vì chúng ta không muốn lớp vỏ trải
rộng đối đó cứ nh là một mẫu tên tệp. Nó phải đợc truyền qua grep để có hiệu quả.)
Thế mà trong Perl, chúng ta có thể nói đích xác cùng điều đó:
while (<>) {
if (/ab*c/) {
print $_;
}
}
Cũng hệt nh grep, điều này có nghĩa là một a theo sau bởi không hay nhiều b,
theo sau là c.
Chúng ta sẽ xem xét nhiều tuỳ chọn khác về toán tử đối sánh trong mục Nói
thêm về toán tử đối sánh, ở cuối chơng này, sau khi ta đã nói về tất cả các loại biểu
thức chính qui.
Một toán tử biểu thức chính qui nữa là toán tử thay thế, làm việc thay thế một
phần của xâu mà sánh đúng biểu thức chính qui bằng một xâu khác. Toán tử thay thế
giống nh chỉ lệnh s trong sed, bao gồm một chữ s, một sổ chéo, một biểu thức chính

qui, một sổ chéo, một xâu thay thế, và một sổ chéo cuối cùng, trông tựa nh thế này:
s/ab*c/def/;
Xâu (trong trờng hợp này là biến $_) đợc đem ra đối sánh với biểu thức chính qui
(ab*c). Nếu việc đối sánh thành công, thì phần của xâu sán hđúng sẽ bị loại ra và đợc
thay thế bằng xâu thay thế (def). Nếu việc đối sánh không thành công thì chẳng có gì
xảy ra cả.
Nh với toán tử đối sánh, ta sẽ còn xem xét lại vô số các tuỳ chọn về toán tử thay
thế dới đây, trong mục Thay thế.
Khuôn mẫu
Một biểu thức chính qui là một khuôn mẫu. Một số phần của khuôn mẫu sánh
đúng chỉ các kí tự trong xâu thuộc kiểu đặc biệt. Những phần khác của khuôn mẫu
sánh đúng cho đa kí tự, hay đa đa kí tự. Trớc hết, ta sẽ xem các khuôn mẫu một kí tự,
rồi đến các khuôn mẫu đa kí tự.
Khuôn mẫu một kí tự
Kí tự sánh mẫu đơn giản nhất và thông dụng nhất trong các biểu thức chính qui là
một kí tự sánh với chính nó. Nói cách khác, đặt một chữ a vào trong biểu thức chính
qui đòi hỏi một chữ tơng ứng a trong xâu.
Kí tự sánh mẫu thông dụng nhất tiếp đó là dấu chấm .. Dấu chấm đối sánh bất
kì kí tự riêng lẻ nào ngoại trừ dấu dòng mới (\n). Chẳng hạn, khuôn mẫu /a./ đối sánh
bất kì dãy hai kí tự nào bắt đầu bằng a và không phải là a\n.
Lớp kí tự sánh mẫu đợc biểu diễn bởi cặp dấu ngoặc vuông mở và đóng, và một
danh sách các kí tự nằm giữa hai dấu ngoặc này. Một và chỉ một trong các kí tự này
phải hiện diện tại phần tơng ứng của xâu cần sánh mẫu. Chẳng hạn,
/[abcde]/
sánh với bất kì một trong năm chữ đầu tiên của bảng chữ thờng, trong khi
/[aeiouAEIOU]/
lại sánh với bất kì năm nguyên âm hoặc chữ thờng hoặcchữ hoa. Nếu bạn muốn
đặt dấu ngoặc vuông phải (]) vào danh sách thì hãy đặt một sổ chéo ngợc ở trớc nó,
hay đặt nó nh kí tự đầu tiên bên trong danh sách. Phạm vi của các kí tự (nh a tới z0 có
thể đợc viết tắt bằng việc chỉ ra những điểm cuối của phạm vi đợc tách biệt bởi dấu

gạch ngang (-); để có đợc hằng kí hiệu gạch ngang, bạn hãy đặt trớc dấu gạch ngang
một sổ chéo ngợc. Sau đây là một số thí dụ khác:
[0123456789] # sánh với mọi chữ số
[0-9] # cũng thế
[0-9\-] # sánh 0-9 hay dấu trừ
[a-z0-9] # sánh bất kì chữ thờng hay số nào
[a-zA-Z0-9_] # sánh bất kì chữ, số hay dấu gạch thấp
Cũng có lớp kí tự bị phủ định, cũng là cùng lớp kí tự, nhng có thêm dấu mũi tên
ngợc (hay dấu mũ ^) đằng trớc, đi ngay sau dấu ngoặc trái. Lớp kí tự này đối sánh với
bất kì kí tự đơn nào không trong danh sách. Chẳng hạn:
[^0-9] # sánh với bất kì kí tự phi số nào
[^aeiouyAEIOUY] # sánh với bất kì kí tự nào không nguyên âm
[^\^] # sánh với một kí tự đơn trừ mũi tên ngợc
Để tiện cho bạn, đã có định nghĩa sẵn một số lớp ksi tự chung, nh đợc mô tả trong
Bảng 7-1.
Bảng 7-1: Viết tắt cho lớp kí tự định sẵn
Kết cấu Lớp tơng
đơng
Kết cấu phủ
định
Lớp phủ
định tơng đ-
ơng
\d (số) [0-9] \D (số,
không!)
[^0-9]
\w (từ) [a-zA-Z0-
9_]
\W (từ,
không!)

[^a-zA-Z0-
9_]
\s
(cách)
[ \r\t\n\f] \S (cách,
không!)
[^ \r\t\n\f]
Khuôn mẫu \d sánh với số. Khuôn mẫu \w sánh với kí tự từ, mặc dầu điều
thực sự sánh đúng là bất kì cái gì hợp lệ trong tên biến Perl. Khuôn mẫu \s sánh với
dấu cách (khoảng trắng), ở đây đợc xác định nh dấu cách, về đầu dòng (không hay
dùng mấy trong UNIX), tab, xuống dòng (dấu dòng mới của UNIX), và kéo giấy. Các
bản chữ hoa sánh đúng với cái đối lập cho những lớp này.
Khuôn mẫu nhóm
Sức mạnh thực sự của biểu thức chính qui là khi bạn có thể nói một hay nhiều
những thứ này hay cho tới năm thứ này. Ta hãy nói về cách thực hiện điều này.
Dãy
Khuôn mẫu nhóm đầu tiên (và có lẽ kém hiển nhiên nhất) là dãy. Điều này có
nghĩa là abc sánh đúng với một a theo sau là b, theo sau là c. Nó dờng nh đơn giản,
nhng tôi cứ đặt tên cho nó để tôi có thể nói về nó sau này.
Bội
Chúng ta đã thấy dấu sao (*) nh một khuôn mẫu nhóm. Dấu sao chỉ ra rằng
không hay nhiều kí tự (hay lớp kí tự) đứng ngay trớc nó.
Hai khuôn mẫu nhóm khác làm việc giống thế là dấu cộng (+), nghĩa là một hay
nhiều kí tự đứng nagy trớc, và dấu hỏi (?), nghĩa là không hay một kí tự ngay trớc.
Chẳng hạn, biểu thức chính qui /fo+ba?r/ sánh đúng cho một f theo sau là một hay
nhiều o, theo sau là a, b và tuỳ chọn a, theo sau là một r.
Trong tất cả ba khuôn mẫu nhóm này, các khuôn mẫu đều tham lam. Nếu một
khuôn mẫu nh vậy có cơ hội sánh đúng giữa năm và mời kí tự thì nó sẽ lọc ra xâu mời
kí tự mỗi lúc. Chẳng hạn:
$_ = fred xxxxxxxxxx barney;

s/x*/boom/;
bao giờ cũng thay tất cả các x liên tiếp bằng boom (kết quả là fred boom barney),
thay vì chỉ thay thế cho một hay hai x, cho dù một tập x ngắn hơn cũng sánh đợc cho
cùng biểu thức chính qui
Nếu bạn cần nói năm tới mời x, thì bạn có thể xoay xở bằng cách đặt năm x
theo sau bởi năm x nữa đi liền sau dấu chấm hỏi. Nhng làm thế trông xấu, mà cũng
chẳng làm việc tốt lắm. Thay vì vậy, có một cách dễ hơn: số bội tổng quát. Số bội
tổng quát bao gồm một cặp dấu ngoặc nhọn với một hay hai số bên trong, nh trong
/x{5,10}/. Giống nh ba số bội khác, kí tự đứng ngay trớc (trong trờng hợp này là chữ
x) phải đợc tìm thấy bên trong số lần lặp đã chỉ ra (năm đến mời ở đây).
Nếu bạn bỏ đi con số thứ hai, nh trong /x{5,}/, thì điều này có nghĩa là nhiều hay
hơn nữa (năm hay nhiều hơn trong trờng hợp này), và nếu bạn bỏ nốt dấu phẩy, nh
trong /x{5}/, thì điều đó có nghĩa là đúng con số này (năm x). Để đợc 5 x hay ít hơn,
bạn phải đặt số không vào, nh trong /x{0,5}/.
Vậy, biểu thức chính qui /a.{5}b/ sánh đúng cho kí tự a đợc tách với ksi tự b bởi
bất kì năm kí tự khác kí tự dòng mới. (Nhớ lại rằng dấu chấm sánh với bất kì kí tự
khác dấu dòng mới, và chúng ta sánh với năm kí tự nh thế ở đây.) Năm kí tự này
không cần phải nh nhau. (Chúng ta sẽ biết cách để buộc chúng là nh nhau trong mục
tiếp.)
Ta có thể miễn trừ hoàn toàn bằng *, +, và ?, vì chúng hoàn toàn tơng đơng với
{0,}, {1,}, và {0,1}. Nhng dễ dàng hơn vẫn là gõ một kí tự ngắt tơng đơng, mà cũng
quen thuộc hơn.
Nếu có hai số bội trong một biểu thức, thì qui tắc tham lam đợc tăng lên với bên
trái nhất là tham nhất. Chẳng hạn:
$_ = a xxx c xxxxxxx d;
/a.*c.*d/;
Trong trờng hợp này, .* thứ nhất trong biểu thức chính qui sánh với tất cả các kí
tự cho tới c thứ hai, cho dù việc sánh đúng chỉ với các kí tự cho tới c đầu tiên vẫn cho
phép toàn bộ biểu thức chính qui đợc sánh. Điều này không tạo ra khác biệt gì (khuôn
mẫu sẽ sánh theo cả hai cách), nhng sau này khi chúng ta có thể nhìn vào các bộ phận

của biểu thức chính qui mà đợc sánh, thì sẽ có đôi chút vấn đề.
Điều gì xảy ra nếu biểu thức xâu và chính qui hơi bị thay đổi đi, chẳng hạn nh:
$_ = a xxx ce xxxxxxx ci xxx d;
/a.*ce.*d/;
Trong trờng hợp này, nếu .* sánh với phần lớn các kí tự có thể trớc c tiếp, thì kí tự
biểu thức chính qui tiếp (e) sẽ không sánh với kí tự tiếp của xâu (i). Trong trờng hợp
này, ta thu đợc việc lần ngợc tự động - số bội bị tháo ra và thử lại, dừng lại tại chỗ
nào đó phía trớc (trong trờng hợp này, tại c trớc, tiếp sau là (e)
*
. Một biểu thức chính
*
*
Về mặt kĩ thuật, có nhiều cách lần ngợc của toán tử * để tìm ra c ở vị trí đầu tiên. Nhng phải hơi thủ thuật hơn để
mô tả nó, mà nó vẫn hoạt động theo cùng nguyên lí.
qui phức tạp có thể bao gồm nhiều mức lần ngợc nh vậy, dẫn tới thời gian thực hiện
lâu.
Dấu ngoặc tròn nh bộ nhớ
Một toán tử nhóm khác là cặp mở và đóng ngoặc tròn quanh bất kì phần khuôn
mẫu nào. Điều này không làm thay đổi liệu khuôn mẫu có sánh đúng hay không, nh-
ng thay vì thế lại làm cho một phần của xâu đợc khuôn mẫu sánh đúng sẽ đợc ghi
nhớ, để cho nó có thể đợc tham khảo tới về sau. Vậy chẳng hạn, (a) vẫn sánh với a,
còn ([a-z]) thì vẫn sánh với bất kì chữ thờng nào.
Để nhớ lại một phần đã ghi nhớ của một xâu, bạn phải đa vào một dáu sổ chéo
ngợc theo sau bởi một số nguyên. Kết cấu khuôn mẫu này biểu thị cho cùng dãy các
kí tự đợc sánh trớc đây trong cặp dấu ngoặc tròn cùng số (đếm từ một) . Chẳng hạn:
/fred(.)barney\1/;
sánh một xâu có chứa fred, tiếp theo là một kí khác dấu dòng mới, tiếp nữa là
barney, rồi tiếp bởi cùng một kí tự đó. Vậy, nó sánh với fredxbarneyx, nhng không
sánh với fredxbarneyy. Bạn hãy so sánh điều đó với:
/fred.barney./

trong đó hai kí tự không xác định có thể là một, hay khác nhau - cũng chẳng
thành vấn đề gì.
Số 1 đến từ đâu vậy? Nó có nghĩa là phần biểu thức chính qui nằm trong dấu
ngoặc đầu tiên. Nếu có nhiều phần nh thế, thì phần thứ hai (đếm các dấu ngặc trái từ
trái sang phải) sẽ đợc tham khảo tới là \2, phần thứ ba là \3, và cứ thế. Chẳng hạn:
/a(.)b(.)c\2d\1/;
sẽ sánh với một a, một kí tự (gọi nó là #1), một b, một kí tự khác (gọi nó là #2),
một c, kí tự #2, một d, và kí tự #1. Cho nên nó sánh với axbycydx, chẳng hạn.
Phần đợc tham khảo tới có thể nhiều hơn một kí tự. Chẳng hạn:
/a(.*)b\1c/;
sánh với một a, theo sau bởi một số bất kì kí tự nào (thậm chí không), theo sau
bởi b, theo sau bởi cùng dãy kí tự đó, theo sau bởi c. Vậy, nó sẽ sánh với
aFREDnFREDc, hay thậm chí abc, nhng không aXXbXXXc.
Một cách dùng khác của phần đợc nhớ của biểu thức chính qui là trong xâu thay
thế của chỉ lệnh thay thế. Kết cấu kiểu \1 vẫn giữ giá trị của chúng trong xâu thay thế,
và có thể đợc tham khảo tới để xây dựng xâu, nh trong:
$_ = a xxx b yyy c zzz d;
s/b(.*)c/d\1e/;
mà sẽ thay thế b và c bằng d và e, vẫn giữ lại phần ở giữa.
Thay phiên

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×