intTypePromotion=1
zunia.vn Tuyển sinh 2024 dành cho Gen-Z zunia.vn zunia.vn
ADSENSE

Learning Perl - Biểu thức chính qui part 1

Chia sẻ: AJFGASKJHF SJHDB | Ngày: | Loại File: PDF | Số trang:5

89
lượt xem
8
download
 
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

7.1 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 c hí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.

Chủ đề:
Lưu

Nội dung Text: Learning Perl - Biểu thức chính qui part 1

  1. D iễn đàn tin học | Tutorial Room Mục lục «« C hương 6 »» C hương 8 Learning Perl - Chương 7: Biểu thức chính qui 1. Nhập v ào từ STDIN 2. Nhập v ào từ toán tử hình thoi 3. Đưa ra STDOUT 4. Bài t ập 7.1 Khái niệm về biểu thức chính qui Biểu t hứ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 c hí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 đíc h xác vào các h thức và nơi c hốn mà biểu thức c hính qui được sánh đúng. Biểu thức c hính qui thường được nhiều c hương trình UNIX dùng tới, như grep, sed, awk, ed, vi, emac s và thậm chí cả nhiều shell sc ript 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. Perl là một siêu tệp ngữ nghĩa c ho tất c ả những c ông cụ này - bất kì biểu t hức chính qui nào mà c ó thể được viết trong một trong những công cụ UNIX này t hì c ũng đều có thể được viết trong Perl, nhưng không nhất thiết dùng hệt các kí t ự đó. 7.2 Cách dùng đơn giản về biểu thức chính qui Nếu c hú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 lệnh grep: grep abc sonefile > result Trong t rường hợp này, abc là biểu thức c hính qui mà lệnh grep lấy để kiểm tra cho từng dòng đưa vào. Những dòng so sánh đúng sẽ được c huyển ra lối ra c huẩn (ở đây, kết quả sẽ được ghi vào t ệp result ). 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 "$_"; } Nhưng cái gì được kiểm tra so với biểu thức c hính qui abc t rong trường hợp này? Tại sao biến $_ lại c ó mặt ở đây? Khi một biểu thức c hính qui được bao t rong hai dấu sổ chéo (như trên), thì biến $_ sẽ được kiểm tra theo biểu thức c hính qui đó. Nếu biểu thức c hính qui so sánh đúng, thì t oán tử so sánh sẽ trả về giá trị đúng; ngược lại nó trả về giá trị sai. Trong thí dụ này, biến $_ được giả sử c ó c hứ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ư lệnh grep ở trên. Không giống như c hỉ lệnh grep, vốn vận hành trên tất c ả c ác dòng c ủa tệp, đoạn c hươ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 c hu 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ố lượng c ác ký tự 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 ó c hứ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, t a phải nói: grep "ab*c" somefile > result Trong Perl, chúng ta c ó thể làm tương tự như sau: while () { if (/ab*c/) { print “$_”; }
  2. } 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ỳ c họn khác về toán tử đối sánh trong mục "Nói t hêm về toán tử đối sánh", ở cuối c hương này, sau khi ta đã nói về tất c ả c ác loại biểu thức c hính qui. Một toán tử biểu thức c hí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 c hính qui bằng một xâu khác . Toán tử t hay thế giống như chỉ lệnh s trong sed, bao gồm một chữ s, một sổ c héo, một biểu thức c hính qui, một sổ chéo, một xâu thay thế, và một sổ chéo cuối cùng, t rô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 c hính qui (ab*c ). Nếu việc đối sánh thành c ông, thì phần c ủa xâu sánh đú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ì c hẳ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ỳ c họn về toán tử t hay thế dưới đây, trong mục "Thay thế". 7.3 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 c hỉ 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 c ho đa kí tự. Trước hết, t a sẽ xem c ác khuôn mẫu một kí tự, rồi đến c ác khuôn mẫu đa kí tự. 7.3.1 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 c hính qui là một kí tự sánh với chính nó. Nói c ách khác, đặt một c hữ a vào trong biểu thức c hính qui đòi hỏi một c hữ tương ứng a trong xâu. Kí tự sánh mẫu thông dụng nhất tiếp đó là dấu c hấm (.). Dấu chấm đối sánh bất kì kí tự riêng lẻ nào ngoại trừ dấu xuống 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ác h c ác kí tự nằm giữa hai dấu ngoặc này. Một và c hỉ 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, /[abc de]/ sẽ sánh đúng với bất kì một trong năm chữ đầu t iên của bảng c hữ thường, trong khi /[aeiouAEIOU]/ lại sánh với bất kì năm nguyên âm hoặc c hữ thường hoặc chữ hoa. Nếu bạn muốn đặt dấu ngoặc vuông phải (]) vào danh sác h thì hãy đặt một sổ chéo ngược ở trước nó (ví dụ \]), hay đặt nó như kí tự đầu tiên bên trong danh sác h. Phạm vi của c ác kí tự (như a tới z) c ó thể được viết tắt bằng việc chỉ ra những điểm c uối c ủa phạm vi được tác h 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ạc h ngang một sổ c héo ngược. Sau đây là một số thí dụ khác : [0123456789] # s ánh với mọi c hữ số # t ương tự nhưu trên [0-9] # s ánh 0-9 hay dấu trừ [0-9\-] # s ánh bất kì c hữ thường hay số nào [a-z0-9] [a-zA-Z0-9_] # s ánh bất kì c hữ, số hay dấu gạc h dưới Cũng có lớp kí tự bị phủ định, c ũng là c ùng lớp kí tự, nhưng c ó thêm 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ác h. Chẳng hạn: # s ánh với bất kì kí tự nào không phải là c hữ số [^0-9] [^aeiouyAEIOUY] # s ánh với bất kì kí tự nào không nguyên âm # s ánh với bất kỳ ký tự nào không phải là dấu mũ [^\^] Để tiện c ho bạn, đã có định nghĩa sẵn một số lớp ký tự chung, như được mô tả t rong Bảng 7-1. Viết tắt Nghĩa \d (số) [0-9]
  3. \D (phủ định c ủa \d) [^0-9] \w (từ) [a-zA-Z0-9_] \W (phủ định c ủa \w) [^a-zA-Z0-9_] \s (khoảng trắng) [ \r\t\n\f] \S (phủ định c ủa \s) [^ \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 ác h" (khoảng trắng), ở đây được xác định như ký tự space, về đầu dòng (ít dùng trong UNIX), tab, xuống dòng (dấu dòng mới c ủa UNIX), và kéo giấy. Các bảng c hữ hoa sánh đúng với cái đối lập c ho những lớp này. 7.3.2 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 "c ho tới năm thứ này". Ta hãy nói về c ách thực hiện điều này. 7.3.2.1 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, nhưng tôi c ứ đặt tên c ho nó để tôi có thể nói v ề nó sau này. 7.3.2.2 Bội Chúng ta đã thấy dấu sao (* ) như một khuôn mẫu nhóm. Dấu * c hỉ 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 ngay 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 c hính qui /fo+ba?r/ s ánh đúng c ho một f theo sau là một hay nhiều o, theo sau là a, b và tuỳ c họ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: $_ = "jerry xxxxxxxxxx tom"; 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à jerry boom tom), thay vì c hỉ thay thế cho một hay hai x, c ho dù một tập x ngắn hơn c ũng sánh được c ho cùng biểu thức c hí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ác h đặt năm x t heo sau bởi năm x nữa đi liền sau dấu c hấm hỏi. Nhưng làm thế trông xấu, mà c ũng c hẳng làm việc tốt lắm. Thay vì vậy, có một c ác h 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, ví dụ /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à c hữ "x") phải được tìm thấy bên trong số lần lặp đã c hỉ ra (năm đến mười ở đây). Nếu bạn bỏ đi c on số thứ hai, như trong /x{5,}/, t hì đ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}/, t hì điều đó c ó nghĩa là "đúng con số này" (đúng 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 c hính qui /a.{5}b/ sánh đúng c ho kí t ự a được tác h với ký tự b bởi bất kì năm kí tự khác kí tự xuống dòng mới (nhớ lại rằng dấu c hấm sánh với bất kì kí tự khác dấu xuống dòng, và c hú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 ác h để buộc c húng là như nhau trong mục tiếp). Ta c ó thể loại bỏ hẳn hoàn toàn * , +, và ?, vì c húng hoàn toàn t ương đương với {0,}, {1,}, và {0,1}. Nhưng 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 tăng được tăng lên với "bên trái nhất là tăng lên
  4. nhất". Chẳng hạn: $_ = "a xxx c xxxx c xxx d"; /a.*c.*d/; Trong trường hợp này, ".* " thứ nhất trong biểu t hức c hính qui sánh với tất cả các kí tự c ho tới c thứ hai, c ho dù việc sánh đúng chỉ với c ác kí tự c ho 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), nhưng sau này khi c húng ta có thể nhìn vào các bộ phận c ủa biểu thức c hí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à c hính qui hơi bị thay đổi đi, c hẳ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 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. 7.3.2.3 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 c ho một phần của xâu được khuôn mẫu sánh đúng sẽ được ghi nhớ, để c ho nó c ó thể được tham khảo tới về sau. Vậy c hẳng hạn, (a) vẫn sánh với a, c òn ([a-z]) t hì vẫn sánh với bất kì c hữ 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ổ c hé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: /jerry(.)tom\1/; Sẽ sánh một xâu có c hứa jerry , tiếp theo là một kí khác dấu xuống dòng, tiếp nữa là t om, rồi tiếp bởi cùng một kí tự đó. Vậy, nó sánh với jerryxtomx, nhưng không sánh với jerryxtomy. Bạn hãy so sánh điều đó với /jerry.tom./ t rong đó hai kí tự không xác định có t hể là một, hay khác nhau - cũng c hẳng thành vấn đề gì; /jerry.tom./ sẽ sánh với c ả jerryxtomx v à jerryxtomy. 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 t rá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 (c ho nên nó sánh với axbycydx c hẳ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ẽ sánh với một a, theo sau bởi một số bất kì kí tự nào (thậm c hí không), t heo 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 c hí abc , nhưng không sánh aXXbXXXc . Một c ác h dùng khác c ủa phần được nhớ của biểu thức c hính qui là trong xâu t hay thế c ủa c hỉ lệnh thay thế. Kết c ấu kiểu \1 vẫn giữ giá trị của c hú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/; sẽ thay thế b và c bằng d và e, vẫn giữ lại phần ở giữa.
  5. 7.3.2.4 Thay phiên Một kết cấu nhóm khác là thay phiên, như trong a|b|c . Điều này c ó nghĩa là sánh đúng một trong c ác khả năng (a hoặc b hoặc c trong trường hợp này). Điều này vẫn có tác dụng ngay cả khi các thay phiên c ó nhiều kí tự, như trong /song|blue/, sẽ sánh hoặc s ong hoặc blue. (với những thay phiên đơn giản, tốt hơn c ả là bạn có thể bỏ lớp kí tự như /[abc ]/). Điều gì xảy ra nếu ta muốn sánh s ongbird hay bluebird? Ta c ó thể viết /songbird|bluebird/, nhưng phần bird đó không nên c ó đó hai lần; tốt hơn ta c ó thể ghi /(song|blue)bird/. Trong thực tế, c ũng c ó c ác h ra, nhưng ta phải nói tới thứ tự ưu tiên c ho c ác khuôn mẫu nhóm, sẽ được đề c ập tới t rong mục "Thứ tự ưu tiên" dưới đây. 7.3.3 Khuôn mẫu neo Bốn kí pháp đặc biệt đóng neo c ho một khuôn mẫu. Thông thường, khi một khuôn mẫu được sánh với xâu thì sự bắt đầu của khuôn mẫu đó được rê đi trong toàn bộ xâu từ trái sang phải, sánh với cơ hội có thể đầu tiên. Neo cũng c ho phép bạn đảm bảo rằng c ác phần c ủa dòng khuôn mẫu sắp thẳng với những phần đặc biệt của xâu. Cặp neo thứ nhất đòi hỏi rằng một phần đặc biệt c ủa việc đối sánh phải được định vị tại biên giới từ hay không tại biên giới từ. Neo \b yêu c ầu một biên giới từ tại điểm đã c hỉ ra c ho khuôn mẫu đối sánh. Biên giới từ là nơi ở giữa c ác kí tự sánh với \w v à \W, hay giữa c ác kí tự sánh với \w v à c hỗ bắt đầu hay kết thúc c ủa xâu. Chú ý rằng điều này ít phải xử lí đối với tiếng Anh và phải làm nhiều đối với c ác kí hiệu C, nhưng điều đó c ũng gần thôi khi ta đạt tới. Chẳng hạn: # s ánh fred, nhưng không frederic k /fred\b/; # s ánh wiz và w izard, nhưng không qwiz /\bwiz/; /\bFred\b/; # s ánh Fred nhưng không Frederic k hay alFred /abc\bdef/; # không bao giờ sánh (không thể c ó c ận ở đây) /\bFred\B/; # s ánh "Frederic k" nhưng không "Fred Flintstonee" Hai neo nữa yêu cầu rằng một phần đặc biệt c ủa khuôn mẫu phải đi ngay sau c uối xâu. Dấu mũ (^ ) sánh với điểm bắt đầu của xâu nếu nó đang ở một vị trí tạo ra nghĩa để đối sánh tại chỗ bắt đầu c ủa xâu. Chẳng hạn, ^a sánh một a nếu và c hỉ nếu a là kí tự đầu tiên c ủa xâu. Tuy nhiên, ^a c ũng sánh với hai kí tự a và ^ ở bất kì đâu trong xâu, nói các h khác, dấu mũ đã mất ý nghĩa đặc biệt của nó. Nếu bạn c ần dấu mũ là một hằng kí hiệu dấu mũ ngay tại chỗ bắt đầu, thì hãy đặt một dấu sổ c héo ngược phía trước nó. Dấu $ c ũng giống như ^, neo lại khuôn mẫu, nhưng t ại cuối c ủa xâu, không phải bắt đầu. nói các h khác , c $ sánh với một c c hỉ nếu nó xuất hiện tại c uối xâu. Dấu $ ở bất kì nơi đâu khác trong khuôn mẫu có lẽ vẫn cứ được diễn giải như c ác h hiểu giá trị vô hướng, c ho nên bạn gần như bao giờ c ũng phải dùng dấu sổ c héo ngược để đối sánh một dấu hiệu đó là hàng kí hiệu trong xâu. 7.3.4 Thứ tự ưu tiên Vậy điều gì xảy ra khi ta lấy a|b* c ùng nhau? Liệu đây là a hay b một số lần bất kì hay c hỉ một a hay nhiều b? Được rồi, c ũng giống như c ác toán tử c ó số ưu tiên, c ác khuôn mẫu bỏ neo và gộp nhóm cũng c ó độ ưu t iên. Độ ưu tiên c ủa khuôn mẫu từ cao xuống thấp nhất được cho trong Bảng 7-2. Bảng 7-2: Số ưu tiên toán tử gộp nhóm biểu thức chính qui (cao nhất xuống thấp nhất) Tên Biểu diễn Dấu ngoặc tròn () Số bội ? + * {m,n} Tuần tự và bỏ neo abc ^ $ \B \b Thay phiên | Theo bảng này, * c ó độ ưu tiên c ao hơn | . Cho nên /a|b*/ được diễn giải như một a, hay số bất kì b. Điều gì xảy ra nếu ta muốn một nghĩa khác, như trong "bất kì số a hay b nào"? Chúng ta đơn thuần c hỉ ném vào một c ặp dấu ngoặc. Trong trường hợp này, bạn hãy bao phần của biểu thức mà toán tử *
ADSENSE

CÓ THỂ BẠN MUỐN DOWNLOAD

 

Đồng bộ tài khoản
2=>2