GoGPT Best VPN GoSearch

Biểu tượng yêu thích OnWorks

perlretut - Trực tuyến trên đám mây

Chạy perlretut trong nhà cung cấp dịch vụ lưu trữ miễn phí OnWorks trên Ubuntu Online, Fedora Online, trình giả lập trực tuyến Windows hoặc trình giả lập trực tuyến MAC OS

Đây là lệnh perlretut có thể chạy trong nhà cung cấp dịch vụ lưu trữ miễn phí OnWorks bằng cách sử dụng một trong nhiều máy trạm trực tuyến miễn phí của chúng tôi như Ubuntu Online, Fedora Online, trình giả lập trực tuyến Windows hoặc trình giả lập trực tuyến MAC OS

CHƯƠNG TRÌNH:

TÊN


perlretut - Hướng dẫn về biểu thức chính quy Perl

MÔ TẢ


Trang này cung cấp hướng dẫn cơ bản về cách hiểu, tạo và sử dụng
biểu thức trong Perl. Nó phục vụ như một phần bổ sung cho trang tham khảo thường xuyên
biểu thức perlre. Biểu thức chính quy là một phần không thể thiếu của "m //", "s ///", "qr //"
và các toán tử "tách" và do đó, hướng dẫn này cũng trùng lặp với "Regexp Quote-Like
Toán tử "trong perlop và" tách "trong perlfunc.

Perl nổi tiếng rộng rãi về sự xuất sắc trong xử lý văn bản và biểu thức chính quy là một trong những
của những yếu tố lớn đằng sau sự nổi tiếng này. Biểu thức chính quy Perl hiển thị hiệu quả và
tính linh hoạt chưa được biết đến trong hầu hết các ngôn ngữ máy tính khác. Nắm vững ngay cả những điều cơ bản về
biểu thức chính quy sẽ cho phép bạn thao tác văn bản một cách dễ dàng đáng ngạc nhiên.

Biểu thức chính quy là gì? Một biểu thức chính quy chỉ đơn giản là một chuỗi mô tả một
mẫu. Các mẫu đang được sử dụng phổ biến ngày nay; ví dụ là các mẫu được nhập vào
công cụ tìm kiếm để tìm các trang web và các mẫu được sử dụng để liệt kê các tệp trong một thư mục, ví dụ:
"ls * .txt" hoặc "dir *. *". Trong Perl, các mẫu được mô tả bởi biểu thức chính quy được sử dụng
để tìm kiếm các chuỗi, trích xuất các phần mong muốn của chuỗi và thực hiện tìm kiếm và thay thế
hoạt động.

Biểu thức chính quy có tiếng là trừu tượng và khó
hiểu biết. Biểu thức chính quy được xây dựng bằng cách sử dụng các khái niệm đơn giản như điều kiện
và lặp lại và không khó hiểu hơn các điều kiện "if" tương ứng
và "while" lặp lại trong chính ngôn ngữ Perl. Trên thực tế, thách thức chính trong việc học
biểu thức chính quy chỉ quen với ký hiệu ngắn gọn được sử dụng để diễn đạt những
các khái niệm.

Hướng dẫn này làm phẳng đường cong học tập bằng cách thảo luận về các khái niệm biểu thức chính quy, cùng
với ký hiệu của chúng, từng cái một và với nhiều ví dụ. Phần đầu tiên của hướng dẫn
sẽ tiến triển từ các tìm kiếm từ đơn giản nhất đến các khái niệm biểu thức chính quy cơ bản.
Nếu bạn nắm vững phần đầu tiên, bạn sẽ có tất cả các công cụ cần thiết để giải quyết khoảng 98%
bạn cần. Phần thứ hai của hướng dẫn dành cho những người hiểu rõ về kiến ​​thức cơ bản và
đói với nhiều công cụ điện hơn. Nó thảo luận về các toán tử biểu thức chính quy nâng cao hơn
và giới thiệu những cải tiến tiên tiến mới nhất.

Lưu ý: để tiết kiệm thời gian, 'biểu thức chính quy' thường được viết tắt là regexp hoặc regex.
Regexp là một từ viết tắt tự nhiên hơn regex, nhưng khó phát âm hơn. Perl
tài liệu pod được chia đều trên regexp vs regex; trong Perl, có nhiều cách
để viết tắt nó. Chúng tôi sẽ sử dụng regexp trong hướng dẫn này.

Tính năng mới trong v5.22, "sử dụng lại 'nghiêm ngặt'" áp dụng các quy tắc chặt chẽ hơn so với khi biên dịch
các mẫu biểu thức chính quy. Nó có thể tìm thấy những thứ, mặc dù hợp pháp, có thể không phải là những gì bạn
dự định.

Phần 1: cơ bản


Đơn giản lời phù hợp
Regexp đơn giản nhất chỉ đơn giản là một từ, hay nói chung là một chuỗi ký tự. Một regexp
bao gồm một từ khớp với bất kỳ chuỗi nào có chứa từ đó:

"Xin chào thế giới" = ~ / World /; # diêm

Câu lệnh Perl này nói về điều gì? "Hello World" là một chuỗi được trích dẫn kép đơn giản.
"World" là biểu thức chính quy và "//" enclosing "/ World /" yêu cầu Perl tìm kiếm một
chuỗi cho một trận đấu. Toán tử "= ~" liên kết chuỗi với đối sánh regexp và
tạo ra giá trị true nếu regexp khớp hoặc false nếu regexp không khớp. Trong của chúng tôi
trường hợp, "World" khớp với từ thứ hai trong "Hello World", do đó, biểu thức là true.
Các biểu thức như thế này rất hữu ích trong các điều kiện:

if ("Hello World" = ~ / World /) {
print "Nó phù hợp với \ n";
}
khác {
print "Nó không khớp \ n";
}

Có những biến thể hữu ích về chủ đề này. Cảm giác của trận đấu có thể bị đảo ngược bởi
sử dụng toán tử "! ~":

if ("Hello World"! ~ / World /) {
print "Nó không khớp \ n";
}
khác {
print "Nó phù hợp với \ n";
}

Chuỗi ký tự trong regexp có thể được thay thế bằng một biến:

$ lời chào = "Thế giới";
if ("Xin chào thế giới" = ~ / $ lời chào /) {
print "Nó phù hợp với \ n";
}
khác {
print "Nó không khớp \ n";
}

Nếu bạn đang khớp với biến mặc định đặc biệt $ _, thì phần "$ _ = ~" có thể là
bỏ qua:

$ _ = "Xin chào Thế giới";
if (/ World /) {
print "Nó phù hợp với \ n";
}
khác {
print "Nó không khớp \ n";
}

Và cuối cùng, các dấu phân cách mặc định "//" cho một kết quả phù hợp có thể được thay đổi thành tùy ý
dấu phân cách bằng cách đặt chữ 'm' ra phía trước:

"Hello World" = ~ m! World !; # trận đấu, được phân tách bằng '!'
"Xin chào Thế giới" = ~ m {Thế giới}; # kết quả phù hợp, lưu ý kết quả phù hợp '{}'
"/ usr / bin / perl"= ~ m" / perl "; # khớp sau '/ usr / bin',
# '/' trở thành một ký tự thông thường

"/ World /", "m! World!" Và "m {World}" đều đại diện cho cùng một thứ. Khi nào, ví dụ, câu trích dẫn
("" ") được sử dụng làm dấu phân cách, dấu gạch chéo '/' trở thành một ký tự bình thường và có thể
được sử dụng trong regexp này mà không gặp sự cố.

Hãy xem xét các regexps khác nhau sẽ khớp với "Hello World" như thế nào:

"Xin chào thế giới" = ~ / world /; # không khớp
"Xin chào Thế giới" = ~ / o W /; # diêm
"Xin chào Thế giới" = ~ / oW /; # không khớp
"Xin chào thế giới" = ~ / World /; # không khớp

"Thế giới" regexp đầu tiên không khớp vì regexp phân biệt chữ hoa chữ thường. Thư hai
regexp khớp vì chuỗi con 'o W' xuất hiện trong chuỗi "Hello World". Vũ trụ
ký tự '' được coi như bất kỳ ký tự nào khác trong regexp và cần để khớp trong
trường hợp này. Việc thiếu ký tự khoảng trắng là lý do khiến regexp thứ ba 'oW' không
cuộc thi đấu. 'Thế giới' regexp thứ tư không khớp vì có khoảng trắng ở cuối
regexp, nhưng không phải ở cuối chuỗi. Bài học ở đây là regexps phải phù hợp với
một phần của chuỗi chính xác để cho tuyên bố là đúng.

Nếu một regexp khớp ở nhiều vị trí trong chuỗi, Perl sẽ luôn khớp ở
điểm sớm nhất có thể trong chuỗi:

"Xin chào Thế giới" = ~ / o /; # khớp với 'o' trong 'Hello'
"Cái mũ đó màu đỏ" = ~ / hat /; # khớp với 'hat' trong 'That'

Liên quan đến khớp ký tự, có một số điểm nữa bạn cần biết.
Trước hết, không phải tất cả các ký tự đều có thể được sử dụng 'nguyên trạng' trong một trận đấu. Một số ký tự, được gọi là
siêu nhân vật, được dành riêng để sử dụng trong ký hiệu regexp. Các siêu ký tự là

{} [] () ^ $. | * +? \

Ý nghĩa của từng điều này sẽ được giải thích trong phần còn lại của hướng dẫn, nhưng đối với
bây giờ, điều quan trọng là chỉ cần biết rằng một siêu ký tự có thể được so khớp bằng cách đặt một
dấu gạch chéo ngược trước nó:

"2 + 2 = 4" = ~ / 2 + 2 /; # không khớp, + là một siêu ký tự
"2 + 2 = 4" = ~ / 2 \ + 2 /; # trận đấu, \ + được coi như một dấu + bình thường
"Khoảng thời gian là [0,1)." = ~ /[0,1)./ # là lỗi cú pháp!
"Khoảng thời gian là [0,1)." = ~ /\[0,1\)\./ # kết quả phù hợp
“#!/ usr / bin / perl"= ~ / #! \/ usr\/ thùng rác\ / perl /; # diêm

Trong regexp cuối cùng, dấu gạch chéo về phía trước '/' cũng được gạch chéo ngược, vì nó được sử dụng để
phân định regexp. Tuy nhiên, điều này có thể dẫn đến LTS (hội chứng nghiêng tăm xỉa răng), và
thường dễ đọc hơn để thay đổi dấu phân cách.

“#!/ usr / bin / perl"= ~ m! # \!/ usr / bin / perl!; # dễ đọc hơn

Ký tự gạch chéo ngược '\' là một ký tự siêu lớn và cần được gạch chéo ngược:

'C: \ WIN32' = ~ / C: \\ WIN /; # diêm

Ngoài các siêu ký tự, có một số ký tự ASCII không có
các ký tự tương đương có thể in được và thay vào đó được đại diện bởi thoát trình tự. Chung
các ví dụ là "\ t" cho tab, "\ n" cho dòng mới, "\ r" cho dấu xuống dòng và "\ a" cho
chuông (hoặc cảnh báo). Nếu chuỗi của bạn được coi là một chuỗi các byte tùy ý,
chuỗi thoát bát phân, ví dụ: "\ 033" hoặc chuỗi thoát thập lục phân, ví dụ: "\ x1B" có thể
là một đại diện tự nhiên hơn cho các byte của bạn. Dưới đây là một số ví dụ về việc trốn thoát:

"1000 \ t2000" = ~ m (0 \ t2) # kết quả phù hợp
"1000 \ n2000" = ~ / 0 \ n20 / # kết quả phù hợp
"1000 \ t2000" = ~ / \ 000 \ t2 / # không khớp, "0" ne "\ 000"
"cat" = ~ / \ o {143} \ x61 \ x74 / # khớp trong ASCII, nhưng một cách lạ
# để đánh vần con mèo

Nếu bạn đã ở quanh Perl một thời gian, tất cả cuộc nói chuyện về trình tự trốn thoát này có vẻ quen thuộc.
Các chuỗi thoát tương tự được sử dụng trong các chuỗi được trích dẫn kép và trên thực tế là các chuỗi regexps trong Perl
hầu hết được coi là chuỗi được trích dẫn kép. Điều này có nghĩa là các biến có thể được sử dụng trong
regexps. Cũng giống như các chuỗi được trích dẫn kép, giá trị của các biến trong
regexp sẽ được thay thế trước khi regexp được đánh giá cho các mục đích phù hợp. Vì vậy, chúng tôi
có:

$ foo = 'nhà';
'housecat' = ~ / $ foo /; # diêm
'cathouse' = ~ / cat $ foo /; # diêm
'housecat' = ~ / $ {foo} cat /; # diêm

Càng xa càng tốt. Với kiến ​​thức ở trên, bạn đã có thể thực hiện tìm kiếm chỉ với
về bất kỳ chuỗi ký tự nào mà bạn có thể mơ ước. Đây là rất đơn giản thi đua của
Chương trình Grep Unix:

% cat> simple_grep
#!/ usr / bin / perl
$ regexp = shift;
trong khi (<>) {
in if / $ regexp /;
}
^D

% chmod + x simple_grep

% simple_grep abba / usr / dict / words
Rác rưởi
cải bắp
cải bắp
Sa-bát
giữ ngày sa-bát
ngày sa-bát
nghỉ phép
bao kiếm
bao kiếm

Chương trình này rất dễ hiểu. “#!/ usr / bin / perl"là cách tiêu chuẩn để gọi một
chương trình perl từ shell. "$ regexp = shift;" lưu đối số dòng lệnh đầu tiên dưới dạng
regexp được sử dụng, để lại phần còn lại của các đối số dòng lệnh được coi là
các tập tin. "while (<>)" lặp lại trên tất cả các dòng trong tất cả các tệp. Đối với mỗi dòng,
"in if / $ regexp /;" in dòng nếu regexp khớp với dòng. Trong dòng này, cả hai
"print" và "/ $ regexp /" sử dụng biến mặc định $ _ một cách ngầm định.

Với tất cả các regexps ở trên, nếu regexp khớp ở bất kỳ vị trí nào trong chuỗi, thì đó là
coi như một trận đấu. Tuy nhiên, đôi khi chúng tôi muốn chỉ định Ở đâu trong chuỗi
regexp nên cố gắng khớp. Để làm điều này, chúng tôi sẽ sử dụng neo siêu ký tự "^" và
"$". Neo "^" có nghĩa là khớp ở đầu chuỗi và neo "$" có nghĩa là
khớp ở cuối chuỗi hoặc trước một dòng mới ở cuối chuỗi. Đây là cách
chúng được sử dụng:

"quản gia" = ~ / keeper /; # diêm
"quản gia" = ~ / ^ keeper /; # không khớp
"quản gia" = ~ / keeper $ /; # diêm
"quản gia \ n" = ~ / keeper $ /; # diêm

Regexp thứ hai không khớp vì "^" ràng buộc "keeper" chỉ khớp ở
đầu chuỗi, nhưng "quản gia" có thủ môn bắt đầu ở giữa. Thứ ba
regexp không khớp, vì "$" ràng buộc "keeper" chỉ khớp ở cuối
chuỗi.

Khi cả "^" và "$" được sử dụng đồng thời, regexp phải khớp với cả
đầu và cuối chuỗi, tức là, regexp khớp với toàn bộ chuỗi. Coi như

"keeper" = ~ / ^ giữ $ /; # không khớp
"keeper" = ~ / ^ keeper $ /; # diêm
"" = ~ / ^ $ /; # ^ $ khớp với một chuỗi trống

Regexp đầu tiên không khớp vì chuỗi có nhiều thứ hơn là "keep". Kể từ khi
regexp thứ hai chính xác là chuỗi, nó khớp. Sử dụng cả "^" và "$" trong regexp
buộc chuỗi hoàn chỉnh phải khớp, do đó, nó cung cấp cho bạn toàn quyền kiểm soát đối với chuỗi nào
phù hợp và không phù hợp. Giả sử bạn đang tìm kiếm một người bạn tên bert, trong một chuỗi
bằng chính anh ấy:

"dogbert" = ~ / bert /; # kết quả phù hợp, nhưng không phù hợp với những gì bạn muốn

"dilbert" = ~ / ^ bert /; # không khớp, nhưng ..
"bertram" = ~ / ^ bert /; # trận đấu, vì vậy vẫn chưa đủ tốt

"bertram" = ~ / ^ bert $ /; # không khớp, tốt
"dilbert" = ~ / ^ bert $ /; # không khớp, tốt
"bert" = ~ / ^ bert $ /; # trận đấu, hoàn hảo

Tất nhiên, trong trường hợp là một chuỗi ký tự, người ta có thể dễ dàng sử dụng chuỗi
so sánh "$ string eq 'bert'" và nó sẽ hiệu quả hơn. Regexp "^ ... $"
thực sự trở nên hữu ích khi chúng tôi thêm các công cụ regexp mạnh mẽ hơn bên dưới.

Sử dụng tính cách các lớp học
Mặc dù người ta đã có thể làm được khá nhiều điều với chuỗi regexps theo nghĩa đen ở trên, nhưng chúng tôi chỉ
làm xước bề mặt của công nghệ biểu thức chính quy. Trong phần này và các phần tiếp theo
chúng tôi sẽ giới thiệu các khái niệm regexp (và các ký hiệu siêu ký tự được liên kết) sẽ cho phép
một regexp để không chỉ đại diện cho một chuỗi ký tự đơn lẻ, mà là một toàn bộ tốt nghiệp lớp XNUMX của họ.

Một trong những khái niệm như vậy là một tính cách tốt nghiệp lớp XNUMX. Một lớp ký tự cho phép một tập hợp các
các ký tự, thay vì chỉ một ký tự đơn lẻ, để khớp tại một điểm cụ thể trong
regexp. Bạn có thể xác định các lớp ký tự tùy chỉnh của riêng mình. Chúng được biểu thị bằng dấu ngoặc
"[...]", với tập hợp các ký tự có thể được khớp bên trong. Đây là một số
ví dụ:

/con mèo/; # khớp với 'cat'
/ [bcr] tại /; # đối sánh với 'bat,' cat 'hoặc' rat '
/ item [0123456789] /; # khớp với 'item0' hoặc ... hoặc 'item9'
"abc" = ~ / [taxi] /; # khớp với 'a'

Trong câu lệnh cuối cùng, mặc dù 'c' là ký tự đầu tiên trong lớp, nhưng 'a' vẫn khớp
bởi vì vị trí ký tự đầu tiên trong chuỗi là điểm sớm nhất mà tại đó
regexp có thể khớp.

/ [yY] [eE] [sS] /; # đối sánh 'yes' theo cách không phân biệt chữ hoa chữ thường
# 'yes', 'Yes', 'YES', v.v.

Regexp này hiển thị một nhiệm vụ chung: thực hiện đối sánh không phân biệt chữ hoa chữ thường. Perl cung cấp một cách
tránh tất cả các dấu ngoặc đó bằng cách chỉ cần thêm 'i' vào cuối trận đấu. sau đó
"/ [yY] [eE] [sS] /;" có thể được viết lại thành "/ yes / i;". Chữ 'i' là viết tắt của phân biệt chữ hoa chữ thường và
là một ví dụ về một thay đổi của hoạt động khớp. Chúng ta sẽ gặp các bổ ngữ khác sau
trong phần hướng dẫn.

Chúng tôi đã thấy trong phần trên rằng có các ký tự bình thường, đại diện cho
và các ký tự đặc biệt, cần có dấu gạch chéo ngược "\" để đại diện cho chính họ.
Điều này cũng đúng trong một lớp nhân vật, nhưng tập hợp các ký tự thông thường và đặc biệt
bên trong một lớp ký tự khác với bên ngoài một lớp ký tự. Cái đặc biệt
các ký tự cho một lớp ký tự là "-] \ ^ $" (và dấu phân cách mẫu, bất kể nó là gì).
"]" đặc biệt vì nó biểu thị phần cuối của một lớp ký tự. "$" đặc biệt vì nó
biểu thị một biến vô hướng. "\" đặc biệt vì nó được sử dụng trong các chuỗi thoát, chỉ
như trên. Đây là cách các ký tự đặc biệt "] $ \" được xử lý:

/ [\] c] def /; # trận đấu với '] def' hoặc 'cdef'
$ x = 'bcr';
/ [$ x] tại /; # đối sánh với 'bat', 'cat' hoặc 'rat'
/ [\ $ x] tại /; # khớp với '$ at' hoặc 'xat'
/ [\\ $ x] tại /; # đối sánh với '\ at', 'bat,' cat 'hoặc' rat '

Hai cách cuối cùng là một chút khó khăn. Trong "[\ $ x]", dấu gạch chéo ngược bảo vệ ký hiệu đô la, vì vậy
lớp ký tự có hai thành viên "$" và "x". Trong "[\\ $ x]", dấu gạch chéo ngược được bảo vệ,
vì vậy $ x được coi là một biến và được thay thế theo kiểu dấu ngoặc kép.

Ký tự đặc biệt '-' hoạt động như một toán tử phạm vi trong các lớp ký tự, do đó
tập ký tự liền nhau có thể được viết dưới dạng một dải ô. Với phạm vi, khó sử dụng
"[0123456789]" và "[abc ... xyz]" trở thành "[0-9]" và "[az]". Một số ví dụ

/ item [0-9] /; # khớp với 'item0' hoặc ... hoặc 'item9'
/ [0-9bx-z] aa /; # đối sánh với '0aa', ..., '9aa',
# 'baa', 'xaa', 'yaa' hoặc 'zaa'
/ [0-9a-fA-F] /; # khớp với một chữ số thập lục phân
/ [0-9a-zA-Z _] /; # khớp với một ký tự "từ",
# giống như những cái đó trong tên biến Perl

Nếu '-' là ký tự đầu tiên hoặc cuối cùng trong một lớp ký tự, nó được coi là ký tự bình thường
tính cách; "[-ab]", "[ab-]" ​​và "[a \ -b]" đều tương đương.

Ký tự đặc biệt "^" ở vị trí đầu tiên của lớp ký tự biểu thị phủ định
tính cách tốt nghiệp lớp XNUMX, khớp với bất kỳ ký tự nào trừ những ký tự trong ngoặc. Cả "[...]" và
"[^ ...]" phải khớp với một ký tự, nếu không kết quả khớp không thành công. sau đó

/ [^ a] tại /; # không khớp với 'aat' hoặc 'at', nhưng khớp với
# tất cả khác 'bat', 'cat,' 0at ','% at ', v.v.
/ [^ 0-9] /; # khớp với một ký tự không phải số
/ [a ^] tại /; # khớp với 'aat' hoặc '^ at'; ở đây '^' là bình thường

Giờ đây, ngay cả "[0-9]" cũng có thể khiến bạn phải phiền lòng khi viết nhiều lần, vì vậy, vì lợi ích của việc tiết kiệm
các tổ hợp phím và làm cho regexps dễ đọc hơn, Perl có một số từ viết tắt phổ biến
các lớp ký tự, như được hiển thị bên dưới. Kể từ khi Unicode ra đời, trừ khi "// a"
công cụ sửa đổi đang có hiệu lực, những lớp ký tự này phù hợp với nhiều hơn chỉ một vài ký tự trong
phạm vi ASCII.

· \ D đối sánh với một chữ số, không chỉ [0-9] mà còn cả các chữ số từ các tập lệnh không phải la mã

· \ S khớp với một ký tự khoảng trắng, tập hợp [\ \ t \ r \ n \ f] và các ký tự khác

· \ W đối sánh với một ký tự từ (chữ và số hoặc _), không chỉ [0-9a-zA-Z_] mà còn cả các chữ số
và các ký tự từ các chữ viết không phải la mã

· \ D là một \ d bị phủ định; nó đại diện cho bất kỳ ký tự nào khác ngoài một chữ số hoặc [^ \ d]

· \ S là một \ s bị phủ định; nó đại diện cho bất kỳ ký tự không có khoảng trắng nào [^ \ s]

· \ W là một \ w bị phủ định; nó đại diện cho bất kỳ ký tự không phải từ nào [^ \ w]

· Dấu chấm '.' khớp với bất kỳ ký tự nào trừ "\ n" (trừ khi công cụ sửa đổi "// s" có hiệu lực,
như giải thích bên dưới).

· \ N, giống như dấu chấm, khớp với bất kỳ ký tự nào trừ "\ n", nhưng nó không giống như vậy bất kể
liệu công cụ sửa đổi "// s" có hiệu lực hay không.

Bổ ngữ "// a", có sẵn bắt đầu trong Perl 5.14, được sử dụng để hạn chế các kết quả phù hợp của
\ d, \ s và \ w tới chỉ những người trong phạm vi ASCII. Sẽ rất hữu ích nếu giữ chương trình của bạn không
không cần thiết phải tiếp xúc với Unicode đầy đủ (và các cân nhắc bảo mật đi kèm của nó)
khi tất cả những gì bạn muốn là xử lý văn bản giống tiếng Anh. ("A" có thể được nhân đôi, "// aa", thành
cung cấp nhiều hạn chế hơn nữa, ngăn chặn việc đối sánh không phân biệt chữ hoa chữ thường của ASCII với không phân biệt chữ hoa chữ thường
Ký tự ASCII; nếu không, một "Kí hiệu Kelvin" Unicode sẽ đối sánh không kỳ hạn với "k" hoặc "K".)

Các chữ viết tắt "\ d \ s \ w \ D \ S \ W" có thể được sử dụng cả bên trong và bên ngoài của dấu ngoặc
các lớp nhân vật. Dưới đây là một số đang được sử dụng:

/ \ d \ d: \ d \ d: \ d \ d /; # khớp với định dạng thời gian hh: mm: ss
/ [\ d \ s] /; # khớp với bất kỳ chữ số hoặc ký tự khoảng trắng nào
/ \ w \ W \ w /; # khớp với một ký tự từ, theo sau là một
# ký tự không phải từ, theo sau là ký tự từ
/..rt/; # khớp với hai ký tự bất kỳ, theo sau là 'rt'
/chấm dứt\./; # khớp với 'end.'
/chấm dứt[.]/; # điều tương tự, khớp với 'end'.

Bởi vì một dấu chấm là một siêu ký tự, nó cần phải được thoát ra để khớp như một dấu chấm bình thường
giai đoạn = Stage. Vì, ví dụ: "\ d" và "\ w" là các bộ ký tự, nó không chính xác khi
nghĩ về "[^ \ d \ w]" là "[\ D \ W]"; trên thực tế "[^ \ d \ w]" giống như "[^ \ w]", là
giống như "[\ W]". Hãy suy nghĩ các định luật DeMorgan.

Trên thực tế, dấu chấm và chữ viết tắt "\ d \ s \ w \ D \ S \ W" tự chúng là các loại
các lớp ký tự, vì vậy những lớp được bao quanh bởi dấu ngoặc chỉ là một loại ký tự
lớp học. Khi cần phân biệt, chúng tôi gọi chúng là "ký tự trong ngoặc
các lớp học."

Một mỏ neo hữu ích trong regexps cơ bản là lời neo "\ b". Điều này phù hợp với một ranh giới
giữa một ký tự từ và một ký tự không phải từ "\ w \ W" hoặc "\ W \ w":

$ x = "Housecat cung cấp nhà và mèo";
$ x = ~ / con mèo /; # khớp với mèo trong 'housecat'
$ x = ~ / \ bcat /; # phù hợp với mèo trong 'ăn uống'
$ x = ~ / cat \ b /; # khớp với mèo trong 'housecat'
$ x = ~ / \ bcat \ b /; # khớp với 'cat' ở cuối chuỗi

Lưu ý trong ví dụ cuối cùng, phần cuối của chuỗi được coi là ranh giới từ.

Để xử lý ngôn ngữ tự nhiên (ví dụ như dấu nháy đơn được bao gồm trong các từ),
thay vào đó sử dụng "\ b {wb}"

"đừng" = ~ /. +? \ b {wb} / x; # khớp với toàn bộ chuỗi

Bạn có thể thắc mắc tại sao '.' phù hợp với mọi thứ trừ "\ n" - tại sao không phải mọi ký tự? Nguyên nhân
đó thường là một đối sánh với các dòng và muốn bỏ qua dòng mới
nhân vật. Ví dụ: trong khi chuỗi "\ n" đại diện cho một dòng, chúng tôi muốn
nghĩ về nó như là trống rỗng. sau đó

"" = ~ / ^ $ /; # diêm
"\ n" = ~ / ^ $ /; # trận đấu, $ neo trước "\ n"

"" = ~ /. /; # không khớp; nó cần một char
"" = ~ /^.$/; # không khớp; nó cần một char
"\ n" = ~ /^.$/; # không khớp; nó cần một ký tự khác ngoài "\ n"
"a" = ~ /^.$/; # diêm
"a \ n" = ~ /^.$/; # trận đấu, $ neo trước "\ n"

Hành vi này rất tiện lợi, bởi vì chúng tôi thường muốn bỏ qua các dòng mới khi chúng tôi đếm và
khớp các ký tự trong một dòng. Tuy nhiên, đôi khi chúng tôi muốn theo dõi các dòng mới. chúng tôi
thậm chí có thể muốn "^" và "$" cố định ở đầu và cuối dòng trong chuỗi,
thay vì chỉ phần đầu và phần cuối của chuỗi. Perl cho phép chúng tôi lựa chọn giữa
bỏ qua và chú ý đến các dòng mới bằng cách sử dụng các bổ ngữ "// s" và "// m". "//S"
và "// m" là viết tắt của một dòng và nhiều dòng và chúng xác định xem một chuỗi có phải là
được coi như một chuỗi liên tục hoặc một tập hợp các dòng. Hai bổ ngữ ảnh hưởng đến hai
các khía cạnh về cách regexp được diễn giải: 1) cách ký tự '.' lớp ký tự được xác định, và
2) trong đó các neo "^" và "$" có thể khớp với nhau. Đây là bốn điều có thể
kết hợp:

· No modifier (//): Hành vi mặc định. '.' khớp với bất kỳ ký tự nào ngoại trừ "\ n". "^"
chỉ khớp ở đầu chuỗi và "$" chỉ khớp ở cuối hoặc trước
một dòng mới ở cuối.

· S modifier (// s): Coi chuỗi như một dòng dài. '.' phù hợp với bất kỳ ký tự nào, thậm chí
"\n". "^" chỉ khớp ở đầu chuỗi và "$" chỉ khớp ở cuối
hoặc trước một dòng mới ở cuối.

· M modifier (// m): Coi chuỗi như một tập hợp nhiều dòng. '.' phù hợp với bất kỳ ký tự nào
ngoại trừ "\ n". "^" và "$" có thể khớp ở đầu hoặc cuối của bất kì dòng trong
chuỗi.

· Cả bổ ngữ s và m (// sm): Coi chuỗi là một dòng dài duy nhất, nhưng phát hiện nhiều
các dòng. '.' khớp với bất kỳ ký tự nào, ngay cả "\ n". Tuy nhiên, "^" và "$" có thể khớp với nhau
ở đầu hoặc cuối của bất kì dòng trong chuỗi.

Dưới đây là các ví dụ về hoạt động của "// s" và "// m":

$ x = "Đã từng có một cô gái \ nNgười được lập trình trong Perl \ n";

$ x = ~ / ^ Ai /; # không khớp, "Ai" không ở đầu chuỗi
$ x = ~ / ^ Ai / s; # không khớp, "Ai" không ở đầu chuỗi
$ x = ~ / ^ Ai / m; # trận đấu, "Ai" ở đầu dòng thứ hai
$ x = ~ / ^ Ai / sm; # trận đấu, "Ai" ở đầu dòng thứ hai

$ x = ~ / cô gái.Who/; # không khớp, "." không khớp với "\ n"
$ x = ~ /girl.Who/s; # diêm, "." khớp với "\ n"
$ x = ~ / cô gái.Who/m; # không khớp, "." không khớp với "\ n"
$ x = ~ /girl.Who/sm; # diêm, "." khớp với "\ n"

Hầu hết thời gian, hành vi mặc định là những gì được mong muốn, nhưng "// s" và "// m" là
đôi khi rất hữu ích. Nếu "// m" đang được sử dụng, phần bắt đầu của chuỗi vẫn có thể là
được khớp với "\ A" và phần cuối của chuỗi vẫn có thể được khớp với các neo "\ Z"
(đối sánh với cả dòng cuối và dòng mới trước đó, chẳng hạn như "$") và "\ z" (chỉ khớp với phần cuối):

$ x = ~ / ^ Ai / m; # trận đấu, "Ai" ở đầu dòng thứ hai
$ x = ~ / \ AWho / m; # không khớp, "Ai" không ở đầu chuỗi

$ x = ~ / cô gái $ / m; # trận đấu, "cô gái" ở cuối dòng đầu tiên
$ x = ~ / cô gái \ Z / m; # không khớp, "girl" không nằm ở cuối chuỗi

$ x = ~ / Perl \ Z / m; # trận đấu, "Perl" ở dòng mới trước khi kết thúc
$ x = ~ / Perl \ z / m; # không khớp, "Perl" không nằm ở cuối chuỗi

Bây giờ chúng ta biết cách tạo các lựa chọn giữa các lớp ký tự trong một regexp. Thế còn
lựa chọn giữa các từ hoặc chuỗi ký tự? Những lựa chọn như vậy được mô tả trong phần tiếp theo.

Phù hợp điều này or việc này
Đôi khi chúng tôi muốn regexp của chúng tôi có thể khớp với các từ khác nhau có thể có hoặc
chuỗi ký tự. Điều này được thực hiện bằng cách sử dụng luân phiên siêu ký tự "|". Đến
so khớp "dog" hoặc "cat", chúng ta tạo thành regexp "dog | cat". Như trước đây, Perl sẽ cố gắng khớp với
regexp tại điểm sớm nhất có thể trong chuỗi. Tại mỗi vị trí ký tự, Perl
trước tiên sẽ cố gắng so khớp phương án thay thế đầu tiên, "dog". Nếu "dog" không khớp, Perl sẽ
sau đó thử thay thế tiếp theo, "con mèo". Nếu "mèo" cũng không khớp, thì trận đấu không thành công
và Perl di chuyển đến vị trí tiếp theo trong chuỗi. Vài ví dụ:

"mèo và chó" = ~ / cat | dog | bird /; # đối sánh với "cat"
"mèo và chó" = ~ / dog | cat | bird /; # đối sánh với "cat"

Mặc dù "dog" là lựa chọn thay thế đầu tiên trong regexp thứ hai, "cat" vẫn có thể khớp
trước đó trong chuỗi.

"cat" = ~ / c | ca | cat | cat /; # khớp với "c"
"cat" = ~ / cat | cat | ca | c /; # đối sánh với "mèo"

Ở đây, tất cả các lựa chọn thay thế đều khớp ở vị trí chuỗi đầu tiên, vì vậy, lựa chọn thay thế đầu tiên là
một trong những phù hợp. Nếu một số lựa chọn thay thế là sự cắt ngắn của những lựa chọn khác, hãy đặt
những người dài nhất đầu tiên để cho họ có cơ hội so khớp.

"cab" = ~ / a | b | c / # khớp với "c"
# / a | b | c / == / [abc] /

Ví dụ cuối cùng chỉ ra rằng các lớp ký tự giống như sự thay thế của các ký tự.
Tại một vị trí ký tự nhất định, giải pháp thay thế đầu tiên cho phép đối sánh regexp với
thành công sẽ là một trong những phù hợp.

Nhóm điều thứ bậc phù hợp
Sự thay thế cho phép một regexp lựa chọn trong số các lựa chọn thay thế, nhưng bản thân nó là
không hài lòng. Lý do là mỗi giải pháp thay thế là một regexp toàn bộ, nhưng đôi khi chúng tôi muốn
các lựa chọn thay thế chỉ cho một phần của regexp. Ví dụ: giả sử chúng ta muốn tìm kiếm
gia chủ hoặc quản gia. Regexp "housecat | housekeeper" phù hợp với hóa đơn, nhưng
không hiệu quả vì chúng tôi đã phải gõ "house" hai lần. Sẽ rất tuyệt nếu có các phần của
regexp là hằng số, như "house" và một số phần có các lựa chọn thay thế, chẳng hạn như "cat | keeper".

nhóm lại metacharacters "()" giải quyết vấn đề này. Việc nhóm cho phép các phần của regexp
được coi như một đơn vị duy nhất. Các phần của regexp được nhóm lại bằng cách đặt chúng trong
dấu ngoặc đơn. Do đó, chúng tôi có thể giải quyết "housecat | housekeeper" bằng cách tạo regexp như
"nhà (mèo | người giữ)". Regexp "house (cat | keeper)" có nghĩa là so khớp với "ngôi nhà", theo sau là
hoặc "mèo" hoặc "thủ môn". Một số ví dụ khác là

/ (a | b) b /; # khớp với 'ab' hoặc 'bb'
/ (ac | b) b /; # khớp với 'acb' hoặc 'bb'
/ (^ a | b) c /; # khớp với 'ac' ở đầu chuỗi hoặc 'bc' ở bất kỳ đâu
/ (a | [bc]) d /; # khớp với 'ad', 'bd' hoặc 'cd'

/ house (cat |) /; # khớp với 'housecat' hoặc 'house'
/ house (cat (s |) |) /; # khớp với 'housecats' hoặc 'housecat' hoặc
# 'nhà ở'. Các nhóm ghi chú có thể được lồng vào nhau.

/ (19 | 20 |) \ d \ d /; # khớp với năm 19xx, 20xx hoặc sự cố Y2K, xx
"20" = ~ / (19 | 20 |) \ d \ d /; # khớp với phương án thay thế null '() \ d \ d',
# vì '20 \ d \ d 'không thể khớp

Sự thay thế hoạt động theo cùng một cách trong các nhóm như ngoài nhóm: tại một vị trí chuỗi nhất định,
thay thế ngoài cùng bên trái cho phép regexp khớp được thực hiện. Vì vậy, trong ví dụ cuối cùng tại
vị trí chuỗi đầu tiên, "20" khớp với vị trí thay thế thứ hai, nhưng không còn gì
để khớp với hai chữ số tiếp theo "\ d \ d". Vì vậy, Perl chuyển sang phương án thay thế tiếp theo,
là phương án thay thế rỗng và điều đó hoạt động, vì "20" là hai chữ số.

Quá trình thử một phương án thay thế, xem nó có phù hợp không và chuyển sang phương án tiếp theo
thay thế, trong khi quay lại chuỗi từ nơi thay thế trước đó đã được thử,
nếu nó không, được gọi là quay lui. Thuật ngữ 'backtracking' xuất phát từ ý tưởng rằng
khớp với một regexp giống như một cuộc dạo chơi trong rừng. Đối sánh thành công một regexp giống như
đến một điểm đến. Có thể có nhiều đầu đường mòn, một cho mỗi chuỗi
và từng vị trí được thử theo thứ tự, từ trái sang phải. Từ mỗi đầu đường mòn có thể có
nhiều con đường, một số trong số đó đưa bạn đến đó, và một số là ngõ cụt. Khi bạn đi
dọc theo một con đường mòn và đi vào ngõ cụt, bạn phải quay lại dọc theo con đường mòn để đến
điểm để thử một con đường mòn khác. Nếu bạn đến đích, bạn dừng lại ngay lập tức và quên mất
về việc thử tất cả các con đường mòn khác. Bạn kiên trì, và chỉ khi bạn đã thử tất cả
các con đường mòn từ tất cả các đầu đường mòn và không đến đích của bạn, bạn có tuyên bố không
thất bại. Cụ thể, đây là phân tích từng bước về những gì Perl làm khi nó cố gắng
để khớp với regexp

"abcde" = ~ / (Abd | abc) (df | d | de) /;

0 Bắt đầu bằng chữ cái đầu tiên trong chuỗi 'a'.

1 Hãy thử phương án thay thế đầu tiên trong nhóm đầu tiên là '' ''.

2 Ghép 'a' theo sau bởi 'b'. Càng xa càng tốt.

3 'd' trong regexp không khớp với 'c' trong chuỗi - một dấu chấm hết. Vì vậy, backtrack hai
và chọn lựa chọn thay thế thứ hai trong nhóm đầu tiên 'abc'.

4 Ghép 'a' theo sau bởi 'b' theo sau là 'c'. Chúng tôi đang trên đà phát triển và đã làm hài lòng
nhóm đầu tiên. Đặt $ 1 thành 'abc'.

5 Chuyển sang nhóm thứ hai và chọn 'df' thay thế đầu tiên.

6 Ghép chữ 'd'.

7 'f' trong regexp không khớp với 'e' trong chuỗi, do đó, một dấu chấm hết. Nhạc nền một
và chọn phương án thay thế thứ hai trong nhóm thứ hai 'd'.

8 'd' phù hợp. Nhóm thứ hai được thỏa mãn, vì vậy hãy đặt $ 2 thành 'd'.

9 Chúng tôi đang ở cuối regexp, vì vậy chúng tôi đã hoàn tất! Chúng tôi đã đối sánh 'abcd' trong số
chuỗi "abcde".

Có một số điều cần lưu ý về phân tích này. Đầu tiên, giải pháp thay thế thứ ba trong
nhóm thứ hai 'de' cũng cho phép đối sánh, nhưng chúng tôi đã dừng lại trước khi đến được - ở một
vị trí nhân vật, ngoài cùng bên trái sẽ thắng. Thứ hai, chúng tôi đã có thể có được một trận đấu ở lần đầu tiên
vị trí ký tự của chuỗi 'a'. Nếu không có kết quả phù hợp nào ở vị trí đầu tiên,
Perl sẽ di chuyển đến vị trí ký tự thứ hai 'b' và thử khớp lại tất cả.
Chỉ khi tất cả các đường có thể có ở tất cả các vị trí ký tự có thể đã được sử dụng hết
Perl từ bỏ và khai báo "$ string = ~ / (Abd | abc) (df | d | de) /;" là sai.

Ngay cả với tất cả công việc này, khớp regexp diễn ra nhanh chóng đáng kể. Để tăng tốc mọi thứ,
Perl biên dịch regexp thành một chuỗi opcodes nhỏ gọn thường có thể vừa với
bộ nhớ đệm của bộ xử lý. Khi mã được thực thi, các mã này sau đó có thể chạy hết tốc lực
và tìm kiếm rất nhanh chóng.

Trích xuất diêm
Nhóm siêu ký tự "()" cũng phục vụ một chức năng hoàn toàn khác: chúng
cho phép trích xuất các phần của một chuỗi đã khớp. Điều này rất hữu ích để tìm
ra những gì phù hợp và để xử lý văn bản nói chung. Đối với mỗi nhóm, phần
đối sánh bên trong đi vào các biến đặc biệt $ 1, $ 2, v.v. Chúng có thể được sử dụng như
biến thông thường:

# trích xuất giờ, phút, giây
if ($ time = ~ / (\ d \ d): (\ d \ d): (\ d \ d) /) {# khớp với định dạng hh: mm: ss
$ giờ = $ 1;
$ phút = $ 2;
$ giây = $ 3;
}

Bây giờ, chúng ta biết rằng trong ngữ cảnh vô hướng, "$ time = ~ / (\ d \ d): (\ d \ d): (\ d \ d) /" trả về true hoặc
giá trị sai. Tuy nhiên, trong ngữ cảnh danh sách, nó trả về danh sách các giá trị phù hợp
"($ 1, $ 2, $ 3)". Vì vậy, chúng tôi có thể viết mã gọn gàng hơn như

# trích xuất giờ, phút, giây
($ giờ, $ phút, $ giây) = ($ time = ~ / (\ d \ d): (\ d \ d): (\ d \ d) /);

Nếu các nhóm trong một regexp được lồng vào nhau, thì $ 1 sẽ nhận được nhóm có lỗ mở ngoài cùng bên trái
dấu ngoặc đơn, $ 2 trong dấu ngoặc đơn mở tiếp theo, v.v. Đây là một regexp với các nhóm lồng nhau:

/ (ab (cd | ef) ((gi) | j)) /;
1 2 34

Nếu regexp này khớp, $ 1 chứa một chuỗi bắt đầu bằng 'ab', $ 2 hoặc được đặt thành 'cd'
hoặc 'ef', $ 3 bằng 'gi' hoặc 'j' và $ 4 được đặt thành 'gi', giống như $ 3, hoặc nó
vẫn chưa được xác định.

Để thuận tiện, Perl đặt $ + thành chuỗi được giữ bởi số $ 1, $ 2, ... được đánh số cao nhất
đã được gán (và, có phần liên quan, $ ^ N cho giá trị của $ 1, $ 2, ... gần đây nhất
giao; tức là $ 1, $ 2, ... được liên kết với dấu ngoặc đóng ngoài cùng bên phải được sử dụng trong
trận đấu).

Tài liệu tham khảo
Được kết hợp chặt chẽ với các biến phù hợp $ 1, $ 2, ... là tài liệu tham khảo "\ g1",
"\ g2", ... Backreferences chỉ đơn giản là các biến đối sánh có thể được sử dụng trong một regexp.
Đây là một tính năng thực sự hay; những gì khớp sau đó trong regexp được tạo ra để phụ thuộc vào những gì
đã khớp trước đó trong regexp. Giả sử chúng tôi muốn tìm kiếm các từ được nhân đôi trong một văn bản,
như 'the'. Regexp sau đây tìm tất cả các chữ cái gấp đôi có 3 chữ cái với khoảng trắng ở giữa:

/ \ b (\ w \ w \ w) \ s \ g1 \ b /;

Việc nhóm sẽ gán một giá trị cho \ g1, để cùng một chuỗi 3 chữ cái được sử dụng cho cả hai
các bộ phận.

Một nhiệm vụ tương tự là tìm các từ bao gồm hai phần giống nhau:

% simple_grep '^ (\ w \ w \ w \ w | \ w \ w \ w | \ w \ w | \ w) \ g1 $' / usr / dict / words
beriberi
Boo Boo
coco

thì thầm
cha

Regexp có một nhóm duy nhất xem xét các kết hợp 4 chữ cái, sau đó là 3 chữ cái
kết hợp, v.v. và sử dụng "\ g1" để tìm kiếm lặp lại. Mặc dù $ 1 và "\ g1" đại diện cho
điều tương tự, cần chú ý chỉ sử dụng các biến được so khớp $ 1, $ 2, ... bên ngoài a
chỉ regexp và backreferences "\ g1", "\ g2", ... trong một regexp; không làm như vậy có thể dẫn đến
kết quả đáng ngạc nhiên và không hài lòng.

Quan hệ tài liệu tham khảo
Đếm các dấu ngoặc đơn mở để có được số chính xác cho một tham chiếu ngược là lỗi-
ngay khi có nhiều hơn một nhóm bắt. Một kỹ thuật tiện lợi hơn
đã có sẵn với Perl 5.10: backreferences tương đối. Để tham khảo ngay
nhóm chụp trước, một nhóm bây giờ có thể viết "\ g {-1}", nhóm tiếp theo nhưng cuối cùng có sẵn qua
"\ g {-2}", v.v.

Một lý do chính đáng khác ngoài khả năng đọc và khả năng bảo trì để sử dụng tương đối
backreferences được minh họa bằng ví dụ sau, trong đó một mẫu đơn giản cho
phù hợp với các chuỗi đặc biệt được sử dụng:

$ a99a = '([az]) (\ d) \ g2 \ g1'; # đối sánh với a11a, g22g, x33x, v.v.

Bây giờ chúng ta đã lưu trữ mẫu này dưới dạng một chuỗi tiện dụng, chúng ta có thể cảm thấy muốn sử dụng nó như
một phần của một số mẫu khác:

$ line = "code = e99e";
if ($ line = ~ / ^ (\ w +) = $ a99a $ /) {# hành vi không mong muốn!
print "$ 1 là hợp lệ \ n";
} Else {
print "bad line: '$ line' \ n";
}

Nhưng điều này không khớp, ít nhất không phải là cách người ta có thể mong đợi. Chỉ sau khi chèn
nội suy $ a99a và nhìn vào văn bản đầy đủ kết quả của regexp, rõ ràng là
backreferences đã phản tác dụng. Biểu thức phụ "(\ w +)" đã giành được số 1 và
đã hạ hạng các nhóm trong $ a99a một bậc. Điều này có thể tránh được bằng cách sử dụng tương đối
tài liệu tham khảo:

$ a99a = '([az]) (\ d) \ g {-1} \ g {-2}'; # an toàn vì được nội suy

Được đặt theo tên tài liệu tham khảo
Perl 5.10 cũng giới thiệu các nhóm chụp được đặt tên và các tham chiếu ngược được đặt tên. Để đính kèm một cái tên
cho một nhóm chụp, bạn viết một trong hai "(? ...) "hoặc" (? 'name' ...) ".
backreference sau đó có thể được viết là "\ g {name}". Được phép đính kèm giống nhau
đặt tên cho nhiều nhóm, nhưng sau đó chỉ có thể đặt tên cho nhóm ngoài cùng bên trái của nhóm cùng tên
được tham chiếu. Bên ngoài mẫu, có thể truy cập nhóm chụp được đặt tên thông qua "% +"
băm.

Giả sử rằng chúng ta phải khớp các ngày lịch có thể được đưa ra ở một trong ba
các định dạng yyyy-mm-dd, mm / dd / yyyy hoặc dd.mm.yyyy, chúng tôi có thể viết ba mẫu phù hợp trong đó
chúng tôi sử dụng 'd', 'm' và 'y' tương ứng làm tên của các nhóm nắm bắt liên quan
các thành phần của một ngày. Hoạt động đối sánh kết hợp ba mẫu làm lựa chọn thay thế:

$ fmt1 = '(? \ d \ d \ d \ d) - (? \ d \ d) - (? \ d \ d) ';
$ fmt2 = '(? \ d \ d) / (? \ d \ d) / (? \ d \ d \ d \ d) ';
$ fmt3 = '(? \ d \ d) \. (? \ d \ d) \. (? \ d \ d \ d \ d) ';
cho $ d qw của tôi (2006-10-21 15.01.2007 10/31/2005) {
nếu ($ d = ~ m {$ fmt1 | $ fmt2 | $ fmt3}) {
print "ngày = $ + {d} tháng = $ + {m} năm = $ + {y} \ n";
}
}

Nếu bất kỳ lựa chọn thay thế nào khớp, hàm băm "% +" bị ràng buộc phải chứa ba khóa-giá trị
cặp.

Thay thế nắm bắt nhóm đánh số
Tuy nhiên, một kỹ thuật đánh số nhóm nắm bắt khác (cũng như từ Perl 5.10) đề cập đến
vấn đề tham chiếu đến các nhóm trong một tập hợp các lựa chọn thay thế. Hãy xem xét một mô hình cho
phù hợp với thời gian trong ngày, phong cách dân sự hoặc quân sự:

if ($ time = ~ / (\ d \ d | \ d): (\ d \ d) | (\ d \ d) (\ d \ d) /) {
# giờ và phút xử lý
}

Việc xử lý kết quả yêu cầu câu lệnh if bổ sung để xác định xem $ 1 và $ 2
hoặc $ 3 và $ 4 chứa các quà tặng. Sẽ dễ dàng hơn nếu chúng ta có thể sử dụng nhóm số 1 và số 2
trong phương án thứ hai cũng vậy, và đây chính xác là những gì mà cấu trúc có dấu ngoặc đơn
"(? | ...)", đặt xung quanh một thành tựu thay thế. Đây là phiên bản mở rộng của phiên bản trước
mẫu:

if ($ time = ~ / (? | (\ d \ d | \ d): (\ d \ d) | (\ d \ d) (\ d \ d)) \ s + ([AZ] [AZ] [ AZ]) /) {
print "giờ = $ 1 phút = $ 2 vùng = $ 3 \ n";
}

Trong nhóm đánh số thay thế, số nhóm bắt đầu ở cùng một vị trí cho mỗi
thay thế. Sau nhóm, đánh số tiếp tục với một cao hơn mức tối đa đã đạt được
trên tất cả các lựa chọn thay thế.

Chức vụ thông tin
Ngoài những gì đã được so khớp, Perl cũng cung cấp các vị trí của những gì đã được so khớp như
nội dung của mảng "@ -" và "@ +". "$ - [0]" là vị trí bắt đầu của toàn bộ
khớp và $ + [0] là vị trí của phần cuối. Tương tự, "$ - [n]" là vị trí của
bắt đầu của trận đấu $ n và $ + [n] là vị trí của kết thúc. Nếu $ n là không xác định, thì
"$ - [n]" và $ + [n]. Sau đó, mã này

$ x = "Mmm ... bánh rán, Homer nghĩ";
$ x = ~ /^(Mmm|Yech)\.\.\.(donut|peas)/; # diêm
foreach $ exp (1 .. $ # -) {
print "Đối sánh $ exp: '$ {$ exp}' tại vị trí ($ - [$ exp], $ + [$ exp]) \ n";
}

in

Khớp 1: 'Mmm' ở vị trí (0,3)
Trận đấu 2: 'donut' ở vị trí (6,11)

Ngay cả khi không có nhóm nào trong regexp, vẫn có thể tìm ra chính xác
khớp trong một chuỗi. Nếu bạn sử dụng chúng, Perl sẽ đặt "$` "thành một phần của chuỗi trước đó
trận đấu, sẽ đặt $ & thành một phần của chuỗi đã khớp và sẽ đặt "$ '" thành
một phần của chuỗi sau trận đấu. Một ví dụ:

$ x = "con mèo bắt được con chuột";
$ x = ~ / con mèo /; # $ `= 'the', $ & = 'cat', $ '=' bắt chuột '
$ x = ~ / cái /; # $ `= '', $ & = 'the', $ '=' mèo bắt được chuột '

Trong kết quả khớp thứ hai, "$` "bằng '' vì regexp khớp ở ký tự đầu tiên
vị trí trong chuỗi và dừng lại; nó không bao giờ nhìn thấy 'cái' thứ hai.

Nếu mã của bạn chạy trên các phiên bản Perl sớm hơn 5.20, bạn cần lưu ý rằng
sử dụng "$` "và" $ '"làm chậm quá trình so khớp regexp một chút, trong khi $ & làm chậm nó xuống một
mức độ thấp hơn, bởi vì nếu chúng được sử dụng trong một regexp trong một chương trình, chúng được tạo ra cho
tất cả các regexps trong chương trình. Vì vậy, nếu hiệu suất thô là mục tiêu của ứng dụng của bạn, chúng
nên tránh. Nếu bạn cần trích xuất các chuỗi con tương ứng, hãy sử dụng "@ -" và "@ +"
thay thế:

$ `giống như substr ($ x, 0, $ - [0])
$ & giống với substr ($ x, $ - [0], $ + [0] - $ - [0])
$ 'giống với substr ($ x, $ + [0])

Kể từ Perl 5.10, các biến "$ {^ PREMATCH}", "$ {^ MATCH}" và "$ {^ POSTMATCH}" có thể là
được sử dụng. Chúng chỉ được đặt nếu có từ bổ nghĩa "/ p". Do đó, họ không
phạt phần còn lại của chương trình. Trong Perl 5.20, "$ {^ PREMATCH}", "$ {^ MATCH}" và
"$ {^ POSTMATCH}" khả dụng cho dù "/ p" đã được sử dụng hay chưa (công cụ sửa đổi là
bỏ qua), và "$` "," $ '"và $ & không gây ra bất kỳ sự khác biệt nào về tốc độ.

Không chụp nhóm
Một nhóm được yêu cầu đóng gói một tập hợp các lựa chọn thay thế có thể hữu ích hoặc có thể không hữu ích như một
nhóm chụp. Nếu không, nó chỉ tạo ra một bổ sung thừa cho tập hợp
các giá trị nhóm nắm bắt có sẵn, bên trong cũng như bên ngoài regexp. Không chụp
các nhóm, được ký hiệu bằng "(?: regexp)", vẫn cho phép regexp được coi như một đơn vị duy nhất,
nhưng không thành lập một nhóm bắt cùng một lúc. Cả chụp và không chụp
các nhóm được phép cùng tồn tại trong cùng một regexp. Bởi vì không có trích xuất,
các nhóm không chụp nhanh hơn so với các nhóm chụp. Các nhóm không chụp là
cũng hữu ích để chọn chính xác các phần của regexp sẽ được trích xuất để phù hợp
biến:

# khớp với một số, $ 1- $ 4 được đặt, nhưng chúng tôi chỉ muốn $ 1
/ ([+ -]? \ * (\ d + (\. \ d *)? | \. \ d +) ([eE] [+ -]? \ d +)?) /;

# khớp một số nhanh hơn, chỉ $ 1 được đặt
/ ([+ -]? \ * (?: \ d + (?: \. \ d *)? | \. \ d +) (?: [eE] [+ -]? \ d +)?) /;

# khớp với một số, nhận được $ 1 = số nguyên, $ 2 = số mũ
/ ([+ -]? \ * (?: \ d + (?: \. \ d *)? | \. \ d +) (?: [eE] ([+ -]? \ d +))?) /;

Các nhóm không chụp cũng hữu ích để loại bỏ các yếu tố phiền toái được thu thập từ một
hoạt động phân tách trong đó dấu ngoặc đơn được yêu cầu vì một số lý do:

$ x = '12aba34ba5';
@num = split / (a ​​| b) + /, $ x; # @num = ('12', 'a', '34', 'a', '5')
@num = split / (?: a | b) + /, $ x; # @num = ('12', '34', '5')

Trong Perl 5.22 trở lên, tất cả các nhóm trong regexp có thể được đặt thành không chụp bằng cách sử dụng
cờ "/ n" mới:

"xin chào" = ~ / (chào | xin chào) / n; # $ 1 chưa được đặt!

Xem "n" trong perlre để biết thêm thông tin.

Phù hợp buổi diễn tập
Các ví dụ trong phần trước hiển thị một điểm yếu khó chịu. Chúng tôi chỉ phù hợp
Từ 3 chữ cái, hoặc nhiều từ 4 chữ cái trở xuống. Chúng tôi muốn có thể kết hợp
từ hoặc nói chung là các chuỗi có độ dài bất kỳ, mà không viết ra các lựa chọn thay thế tẻ nhạt
như "\ w \ w \ w \ w | \ w \ w \ w | \ w \ w | \ w".

Đây chính xác là vấn đề định lượng siêu ký tự "?", "*", "+" và "{}" là
Tạo cho. Chúng cho phép chúng tôi phân định số lần lặp lại cho một phần của regexp mà chúng tôi
coi là một trận đấu. Các đại lượng được đặt ngay sau ký tự, ký tự
lớp hoặc nhóm mà chúng tôi muốn chỉ định. Chúng có những ý nghĩa sau:

· "Một?" có nghĩa là: so khớp 'a' 1 hoặc 0 lần

· "A *" có nghĩa là: so khớp 'a' 0 lần trở lên, tức là bất kỳ số lần nào

· "A +" có nghĩa là: so khớp 'a' 1 hoặc nhiều lần, tức là ít nhất một lần

· "A {n, m}" có nghĩa là: so khớp ít nhất "n" lần, nhưng không nhiều hơn "m" lần.

· "A {n,}" có nghĩa là: so khớp ít nhất "n" lần trở lên

· "A {n}" có nghĩa là: khớp chính xác "n" lần

Dưới đây là một số ví dụ:

/ [az] + \ s + \ d * /; # khớp với một từ viết thường, ít nhất một dấu cách và
# bất kỳ số chữ số nào
/ (\ w +) \ s + \ g1 /; # khớp các từ được nhân đôi có độ dài tùy ý
/Vâng; # khớp với 'y', 'Y' hoặc 'có' không phân biệt chữ hoa chữ thường
$ năm = ~ / ^ \ d {2,4} $ /; # đảm bảo năm ít nhất là 2 nhưng không nhiều hơn
# hơn 4 chữ số
$ năm = ~ / ^ \ d {4} $ | ^ \ d {2} $ /; # phù hợp hơn; ném ra các ngày có 3 chữ số
$ năm = ~ / ^ \ d {2} (\ d {2})? $ /; # cùng một điều được viết khác nhau.
# Tuy nhiên, điều này chụp hai bức ảnh cuối cùng
# chữ số bằng $ 1 và chữ số kia thì không.

% simple_grep '^ (\ w +) \ g1 $' / usr / dict / words # điều này không dễ dàng hơn sao?
beriberi
Boo Boo
coco

thì thầm
cha

Đối với tất cả các bộ định lượng này, Perl sẽ cố gắng khớp càng nhiều chuỗi càng tốt,
trong khi vẫn cho phép regexp thành công. Vì vậy, với "/a?.../", Perl đầu tiên sẽ cố gắng
so khớp regexp với hiện tại "a"; nếu không thành công, Perl sẽ cố gắng khớp với regexp
không có hiện tại "a". Đối với bộ định lượng "*", chúng tôi nhận được như sau:

$ x = "con mèo đội mũ";
$ x = ~ /^(.*)(cat)(.*)$/; # diêm,
# $ 1 = 'the'
# $ 2 = 'con mèo'
# $ 3 = 'trong mũ'

Đó là những gì chúng tôi có thể mong đợi, trận đấu tìm thấy "con mèo" duy nhất trong chuỗi và khóa vào
nó. Tuy nhiên, hãy xem xét regexp này:

$ x = ~ /^(.*)(at)(.*)$/; # diêm,
# $ 1 = 'con mèo trong h'
# $ 2 = 'lúc'
# $ 3 = '' (0 ký tự khớp nhau)

Ban đầu, người ta có thể đoán rằng Perl sẽ tìm thấy "at" trong "cat" và dừng lại ở đó, nhưng điều đó
sẽ không cung cấp chuỗi dài nhất có thể cho bộ định lượng đầu tiên ". *". Thay vào đó,
bộ định lượng đầu tiên ". *" lấy càng nhiều chuỗi càng tốt trong khi vẫn có
trận đấu regexp. Trong ví dụ này, điều đó có nghĩa là có chuỗi "at" với cuối cùng là "at" ở
chuỗi. Nguyên tắc quan trọng khác được minh họa ở đây là, khi có hai hoặc
nhiều phần tử hơn trong một regexp, ngoài cùng bên trái bộ định lượng, nếu có, có thể lấy càng nhiều
của chuỗi càng tốt, để phần còn lại của regexp chiến đấu với các mẩu tin lưu niệm. Do đó trong
ví dụ của chúng tôi, bộ định lượng đầu tiên ". *" lấy hầu hết chuỗi, trong khi chuỗi thứ hai
định lượng ". *" nhận được chuỗi trống. Các bộ định lượng lấy càng nhiều chuỗi như
có thể được gọi là tối đa phù hợp với or tham lam bộ định lượng.

Khi một regexp có thể khớp với một chuỗi theo nhiều cách khác nhau, chúng ta có thể sử dụng các nguyên tắc
ở trên để dự đoán cách regexp sẽ khớp:

· Nguyên tắc 0: Xét một cách tổng thể, mọi regexp sẽ được đối sánh sớm nhất có thể
vị trí trong chuỗi.

· Nguyên tắc 1: Trong một thay thế "a | b | c ...", thay thế ngoài cùng bên trái cho phép a
trận đấu cho toàn bộ regexp sẽ được sử dụng.

· Nguyên tắc 2: Các định lượng phù hợp tối đa "?", "*", "+" Và "{n, m}" sẽ
khớp chung càng nhiều chuỗi càng tốt trong khi vẫn cho phép toàn bộ regexp
khớp.

· Nguyên tắc 3: Nếu có hai hoặc nhiều phần tử trong một regexp, tham lam ngoài cùng bên trái
bộ định lượng, nếu có, sẽ khớp với nhiều chuỗi nhất có thể trong khi vẫn cho phép
toàn bộ regexp để phù hợp. Bộ định lượng tham lam ngoài cùng bên trái tiếp theo, nếu có, sẽ cố gắng
khớp càng nhiều chuỗi còn lại có sẵn với nó càng tốt, trong khi vẫn
cho phép toàn bộ regexp khớp. Và cứ tiếp tục như vậy, cho đến khi tất cả các phần tử regexp
hài lòng.

Như chúng ta đã thấy ở trên, Nguyên tắc 0 ghi đè các nguyên tắc khác. Regexp sẽ được so khớp như
càng sớm càng tốt, với các nguyên tắc khác xác định cách regexp khớp tại đó
vị trí ký tự sớm nhất.

Dưới đây là một ví dụ về các nguyên tắc này đang hoạt động:

$ x = "Cộng hòa lập trình Perl";
$ x = ~ /^(.+)(e|r)(.*)$/; # diêm,
# $ 1 = 'Cộng hòa lập trình Pe'
# $ 2 = 'r'
# $ 3 = 'l'

Regexp này khớp với vị trí chuỗi sớm nhất, 'T'. Người ta có thể nghĩ rằng "e", là
ngoài cùng bên trái trong sự thay thế, sẽ được so khớp, nhưng "r" tạo ra chuỗi dài nhất trong
bộ định lượng đầu tiên.

$ x = ~ /(m{1,2})(.*)$/; # diêm,
# $ 1 = 'mm'
# $ 2 = 'ing republic of Perl'

Ở đây, Kết quả phù hợp sớm nhất có thể là ở chữ 'm' đầu tiên trong "lập trình". "m {1,2}" là
bộ định lượng đầu tiên, vì vậy nó phải khớp với "mm" cực đại.

$ x = ~ /.* (m {1,2}) (. *) $ /; # diêm,
# $ 1 = 'm'
# $ 2 = 'ing republic of Perl'

Ở đây, regexp khớp ở đầu chuỗi. Bộ định lượng đầu tiên ". *" Lấy làm
càng nhiều càng tốt, chỉ để lại một chữ 'm' duy nhất cho bộ định lượng thứ hai "m {1,2}".

$ x = ~ /(.?)(m{1,2})(.*)$/; # diêm,
# $ 1 = 'a'
# $ 2 = 'mm'
# $ 3 = 'ing republic of Perl'

Nơi đây, ".?" ăn tối đa một ký tự của nó ở vị trí sớm nhất có thể trong chuỗi,
'a' trong "lập trình", để "m {1,2}" có cơ hội khớp với cả hai "m". Cuối cùng,

"aXXXb" = ~ / (X *) /; # đối sánh với $ 1 = ''

vì nó có thể khớp với XNUMX bản sao của 'X' ở đầu chuỗi. Nếu bạn chắc chắn
muốn khớp với ít nhất một 'X', hãy sử dụng "X +", không phải "X *".

Đôi khi lòng tham cũng không tốt. Đôi khi, chúng tôi muốn các bộ định lượng phù hợp với tối thiểu mảnh
của chuỗi, chứ không phải là một đoạn tối đa. Vì mục đích này, Larry Wall đã tạo ra tối thiểu
phù hợp với or không tham lam các định lượng "??", "*?", "+?", và "{}?". Đây là những điều bình thường
định lượng bằng dấu "?" được thêm vào chúng. Chúng có những ý nghĩa sau:

· "Một??" nghĩa là: so khớp 'a' 0 hoặc 1 lần. Thử 0 trước, sau đó 1.

· "Một*?" có nghĩa là: so khớp 'a' 0 lần hoặc nhiều hơn, tức là bất kỳ số lần nào, nhưng ít lần bằng
có thể

· "A +?" có nghĩa là: so khớp 'a' 1 hoặc nhiều lần, tức là, ít nhất một lần, nhưng ít lần như
có thể

· "A {n, m}?" có nghĩa là: so khớp ít nhất "n" lần, không nhiều hơn "m" lần, vài lần như
có thể

· "một,}?" có nghĩa là: so khớp ít nhất "n" lần, nhưng càng ít lần càng tốt

· "một}?" nghĩa là: khớp đúng "n" lần. Bởi vì chúng tôi đối sánh chính xác "n" lần, "a {n}?"
tương đương với "a {n}" và chỉ ở đó để đảm bảo tính nhất quán về mặt ký hiệu.

Hãy xem ví dụ trên, nhưng với các bộ định lượng tối thiểu:

$ x = "Cộng hòa lập trình Perl";
$ x = ~ /^(.+?)(e|r)(.*)$/; # diêm,
# $ 1 = 'Th'
# $ 2 = 'e'
# $ 3 = 'nước cộng hòa lập trình Perl'

Chuỗi tối thiểu sẽ cho phép cả phần bắt đầu của chuỗi "^" và sự thay thế thành
phù hợp là "Th", với thay thế "e | r" phù hợp với "e". Định lượng thứ hai ". *" Là
miễn phí để nuốt phần còn lại của chuỗi.

$ x = ~ /(m{1,2}?)(.*?)$/; # diêm,
# $ 1 = 'm'
# $ 2 = 'ming nước cộng hòa Perl'

Vị trí chuỗi đầu tiên mà regexp này có thể khớp là ở 'm' đầu tiên trong "lập trình".
Tại vị trí này, giá trị nhỏ nhất "m {1,2}?" chỉ khớp với một 'm'. Mặc dù thứ hai
định lượng ". *?" không muốn khớp với ký tự nào, nó bị ràng buộc bởi phần cuối của-
chuỗi ký tự "$" để khớp với phần còn lại của chuỗi.

$ x = ~ /(.*?)(m{1,2}?)(.*)$/; # diêm,
# $ 1 = 'Chương trình'
# $ 2 = 'm'
# $ 3 = 'ming nước cộng hòa Perl'

Trong regexp này, bạn có thể mong đợi bộ định lượng tối thiểu đầu tiên ". *?" để khớp với cái trống
chuỗi, bởi vì nó không bị ràng buộc bởi ký tự "^" để khớp với phần đầu của từ.
Tuy nhiên, nguyên tắc 0 được áp dụng ở đây. Bởi vì toàn bộ regexp có thể khớp
ở đầu chuỗi, nó sẽ khớp ở đầu chuỗi. Vì vậy, đầu tiên
bộ định lượng phải khớp mọi thứ với "m" đầu tiên. Bộ định lượng tối thiểu thứ hai
chỉ khớp với một "m" và bộ định lượng thứ ba khớp với phần còn lại của chuỗi.

$ x = ~ /(.??)(m{1,2})(.*)$/; # diêm,
# $ 1 = 'a'
# $ 2 = 'mm'
# $ 3 = 'ing republic of Perl'

Cũng giống như trong regexp trước, bộ định lượng đầu tiên ". ??" có thể khớp sớm nhất ở vị trí
'a', vì vậy nó có. Bộ định lượng thứ hai là tham lam, vì vậy nó khớp với "mm" và bộ định lượng thứ ba
khớp với phần còn lại của chuỗi.

Chúng ta có thể sửa đổi nguyên tắc 3 ở trên để tính đến các bộ định lượng không tham lam:

· Nguyên tắc 3: Nếu có hai hoặc nhiều phần tử trong một regexp, thì phần tử tham lam ngoài cùng bên trái (không
tham lam) định lượng, nếu có, sẽ khớp với nhiều (ít) chuỗi nhất có thể
trong khi vẫn cho phép toàn bộ regexp khớp. Tham lam ngoài cùng bên trái (không tham lam)
bộ định lượng, nếu có, sẽ cố gắng khớp với nhiều (ít) chuỗi còn lại
có sẵn cho nó càng tốt, trong khi vẫn cho phép toàn bộ regexp khớp. Và vì thế
cho đến khi tất cả các phần tử regexp được thỏa mãn.

Cũng giống như sự thay đổi luân phiên, các bộ định lượng cũng dễ bị bẻ khóa ngược. Đây là một bước-
phân tích từng bước của ví dụ

$ x = "con mèo đội mũ";
$ x = ~ /^(.*)(at)(.*)$/; # diêm,
# $ 1 = 'con mèo trong h'
# $ 2 = 'lúc'
# $ 3 = '' (0 kết quả phù hợp)

0 Bắt đầu bằng chữ cái đầu tiên trong chuỗi 't'.

1 Bộ định lượng đầu tiên '. *' Bắt đầu bằng cách khớp với toàn bộ chuỗi 'con mèo trong
mũ '.

2 'a' trong phần tử regexp 'at' không khớp với phần cuối của chuỗi. Nhạc nền một
nhân vật.

3 'a' trong phần tử regexp 'at' vẫn không khớp với chữ cái cuối cùng của chuỗi 't',
vì vậy hãy quay lại một nhân vật nữa.

4 Bây giờ chúng ta có thể ghép chữ 'a' và chữ 't'.

5 Chuyển sang phần tử thứ ba '. *'. Vì chúng ta ở cuối chuỗi và '. *' Có thể
khớp 0 lần, gán nó là chuỗi trống.

6 Chúng tôi đã hoàn thành!

Hầu hết thời gian, tất cả việc tiến và lùi này diễn ra nhanh chóng và tìm kiếm
là nhanh chóng. Tuy nhiên, có một số regexps bệnh lý có thời gian thực hiện theo cấp số nhân
lớn lên với kích thước của chuỗi. Một cấu trúc điển hình có thể thổi bay vào mặt bạn là
hình thức

/ (a | b +) * /;

Vấn đề là các bộ định lượng không xác định được lồng vào nhau. Có nhiều cách khác nhau để
phân vùng một chuỗi có độ dài n giữa "+" và "*": một lần lặp lại với "b +" của
độ dài n, hai lần lặp lại với độ dài "b +" đầu tiên là k và lần thứ hai với độ dài nk, m
các lần lặp lại mà các bit của nó cộng lại với độ dài n, v.v. Thực tế có một cấp số nhân
các cách để phân vùng một chuỗi dưới dạng một hàm có độ dài của nó. Một regexp có thể gặp may và
kết hợp sớm trong quá trình này, nhưng nếu không có kết quả phù hợp, Perl sẽ cố gắng mỗi khả năng
trước khi bỏ cuộc. Vì vậy, hãy cẩn thận với các "*" ', "{n, m}" và "+" được lồng vào nhau. Quyển sách
Làm chủ Đều đặn Biểu thức của Jeffrey Friedl đưa ra một cuộc thảo luận tuyệt vời về điều này và
các vấn đề hiệu quả khác.

Sở hữu định lượng
Việc quay lui trong quá trình không ngừng tìm kiếm một trận đấu có thể là một sự lãng phí thời gian, đặc biệt
khi trận đấu nhất định thất bại. Hãy xem xét mô hình đơn giản

/ ^ \ w + \ s + \ w + $ /; # một từ, khoảng trắng, một từ

Bất cứ khi nào điều này được áp dụng cho một chuỗi không hoàn toàn đáp ứng mong đợi của mẫu
chẳng hạn như "abc" hoặc "abc def", công cụ regex sẽ quay lại, khoảng một lần cho
mỗi ký tự trong chuỗi. Nhưng chúng tôi biết rằng không có cách nào xung quanh việc lấy tất cả các của
các ký tự từ đầu tiên để khớp với lần lặp lại đầu tiên, tất cả các không gian phải được ăn bởi
phần giữa, và tương tự với từ thứ hai.

Với sự giới thiệu của possessively định lượng trong Perl 5.10, chúng tôi có một cách
hướng dẫn công cụ regex không quay lại, với các bộ định lượng thông thường có dấu "+"
được thêm vào chúng. Điều này khiến họ trở nên tham lam cũng như keo kiệt; một khi họ thành công thì họ sẽ không
trả lại bất cứ điều gì để cho phép một giải pháp khác. Chúng có những ý nghĩa sau:

· "A {n, m} +" có nghĩa là: so khớp ít nhất "n" lần, không nhiều hơn "m" lần, nhiều lần như
có thể, và đừng từ bỏ bất cứ điều gì. "a? +" là viết tắt của "a {0,1} +"

· "A {n,} +" có nghĩa là: so khớp ít nhất "n" lần, nhưng nhiều lần nhất có thể, và không khớp
từ bỏ bất cứ điều gì. "a * +" là viết tắt của "a {0,} +" và "a ++" là viết tắt của "a {1,} +".

· "A {n} +" có nghĩa là: khớp chính xác "n" lần. Nó chỉ ở đó cho sự nhất quán về mặt ký hiệu.

Các bộ định lượng sở hữu này đại diện cho một trường hợp đặc biệt của một khái niệm tổng quát hơn,
độc lập biểu thức con, xem bên dưới.

Như một ví dụ trong đó bộ định lượng sở hữu phù hợp, chúng tôi xem xét đối sánh với một
chuỗi, vì nó xuất hiện trong một số ngôn ngữ lập trình. Dấu gạch chéo ngược được sử dụng như một
ký tự thoát cho biết rằng ký tự tiếp theo sẽ được hiểu theo nghĩa đen, như
một ký tự khác cho chuỗi. Do đó, sau phần trích dẫn mở đầu, chúng tôi mong đợi một
(có thể trống) chuỗi các lựa chọn thay thế: một số ký tự ngoại trừ một trích dẫn không thoát
hoặc dấu gạch chéo ngược hoặc một ký tự thoát.

/"(?:[^"\\]++|\\.)*+"/;

Xây dựng a regexp
Tại thời điểm này, chúng ta đã đề cập đến tất cả các khái niệm regexp cơ bản, vì vậy hãy cung cấp thêm
ví dụ liên quan đến một biểu thức chính quy. Chúng tôi sẽ xây dựng một regexp phù hợp với các số.

Nhiệm vụ đầu tiên trong việc xây dựng regexp là quyết định những gì chúng ta muốn đối sánh và những gì chúng ta muốn
loại trừ. Trong trường hợp của chúng tôi, chúng tôi muốn đối sánh cả số nguyên và số dấu phẩy động và chúng tôi
muốn từ chối bất kỳ chuỗi nào không phải là số.

Nhiệm vụ tiếp theo là chia nhỏ vấn đề thành các vấn đề nhỏ hơn để dễ dàng chuyển đổi
thành một regexp.

Trường hợp đơn giản nhất là số nguyên. Chúng bao gồm một chuỗi các chữ số, với một tùy chọn
ký tên ở phía trước. Các chữ số chúng ta có thể biểu thị bằng "\ d +" và dấu có thể được khớp với
"[+ -]". Do đó, số nguyên regexp là

/ [+ -]? \ d + /; # khớp với số nguyên

Một số dấu phẩy động có thể có một dấu, một phần tích phân, một dấu thập phân, một
phần phân số và một số mũ. Một hoặc nhiều phần này là tùy chọn, vì vậy chúng ta cần
kiểm tra các khả năng khác nhau. Số dấu phẩy động ở dạng thích hợp
bao gồm 123., 0.345, .34, -1e6 và 25.4E-72. Đối với số nguyên, mặt trước đăng xuất là
hoàn toàn tùy chọn và có thể được so khớp bởi "[+ -]?". Chúng ta có thể thấy điều đó nếu không có
số mũ, số dấu phẩy động phải có dấu thập phân, nếu không chúng là số nguyên.
Chúng tôi có thể bị cám dỗ để lập mô hình những thứ này bằng "\ d * \. \ D *", nhưng điều này cũng sẽ chỉ phù hợp với
dấu thập phân duy nhất, không phải là một số. Vì vậy, ba trường hợp của số dấu phẩy động
không có số mũ là

/[+-]?\d+\./; # 1., 321., v.v.
/[+-]?\.\d+/; # .1, .234, v.v.
/[+-]?\d+\.\d+/; # 1.0, 30.56, v.v.

Chúng có thể được kết hợp thành một regexp duy nhất với sự thay thế ba chiều:

/[+-]?(\d+\.\d+|\d+\.|\.\d+)/; # dấu phẩy động, không có số mũ

Trong cách thay thế này, điều quan trọng là phải đặt '\ d + \. \ D +' trước '\ d + \.'. Nếu '\ d + \.' đã
đầu tiên, regexp sẽ khớp với điều đó một cách vui vẻ và bỏ qua phần phân số của con số.

Bây giờ hãy xem xét các số dấu phẩy động với số mũ. Quan sát chính ở đây là cả hai
số nguyên và số có dấu thập phân được phép đứng trước số mũ. sau đó
số mũ, giống như dấu tổng thể, không phụ thuộc vào việc chúng ta có khớp các số với
hoặc không có dấu thập phân, và có thể được 'tách rời' khỏi phần định trị. Hình thức tổng thể của
regexp bây giờ trở nên rõ ràng:

/ ^ (dấu tùy chọn) (số nguyên | fp phần định trị) (số mũ tùy chọn) $ /;

Số mũ là một "e" hoặc "E", theo sau là một số nguyên. Vì vậy, regexp lũy thừa là

/ [eE] [+ -]? \ d + /; # số mũ

Kết hợp tất cả các phần lại với nhau, chúng ta nhận được một regexp khớp với các số:

/^[+-]?(\d+\.\d+|\d+\.|\.\d+|\d+)([eE][+-]?\d+)?$/; # Tà dạ!

Những đoạn regex dài như thế này có thể gây ấn tượng với bạn bè của bạn, nhưng có thể khó giải mã. Trong phức hợp
những tình huống như thế này, công cụ sửa đổi "// x" cho một kết quả phù hợp là vô giá. Nó cho phép một người đặt
gần như khoảng trắng tùy ý và nhận xét thành một regexp mà không ảnh hưởng đến ý nghĩa của chúng.
Sử dụng nó, chúng ta có thể viết lại regexp 'mở rộng' của mình ở dạng dễ chịu hơn

/^
[+ -]? # đầu tiên, khớp với một dấu hiệu tùy chọn
(# sau đó khớp với số nguyên hoặc phần định trị fp:
\ d + \. \ d + # phần định trị có dạng ab
| \ d + \. # phần định trị có dạng a.
| \. \ d + # phần định trị có dạng .b
| \ d + # số nguyên có dạng a
)
([eE] [+ -]? \ d +)? # cuối cùng, tùy chọn khớp với một số mũ
$ / x;

Nếu khoảng trắng chủ yếu là không liên quan, thì làm cách nào để bao gồm các ký tự khoảng trắng trong phần mở rộng
regexp? Câu trả lời là hãy gạch chéo ngược nó '\' hoặc đặt nó trong một lớp ký tự "[]". Như nhau
đối với dấu thăng: sử dụng "\ #" hoặc "[#]". Ví dụ, Perl cho phép một khoảng cách giữa
dấu và phần định trị hoặc số nguyên và chúng ta có thể thêm dấu này vào regexp của mình như sau:

/^
[+ -]? \ * # trước tiên, hãy khớp một dấu tùy chọn * và dấu cách *
(# sau đó khớp với số nguyên hoặc phần định trị fp:
\ d + \. \ d + # phần định trị có dạng ab
| \ d + \. # phần định trị có dạng a.
| \. \ d + # phần định trị có dạng .b
| \ d + # số nguyên có dạng a
)
([eE] [+ -]? \ d +)? # cuối cùng, tùy chọn khớp với một số mũ
$ / x;

Trong biểu mẫu này, sẽ dễ dàng hơn để thấy một cách đơn giản hóa sự thay thế. Các lựa chọn thay thế 1, 2,
và 4 tất cả đều bắt đầu bằng "\ d +", vì vậy nó có thể được tính toán:

/^
[+ -]? \ * # trước tiên, hãy đối sánh với một dấu hiệu tùy chọn
(# sau đó khớp với số nguyên hoặc phần định trị fp:
\ d + # bắt đầu bằng ...
(
\. \ d * # phần định trị có dạng ab hoặc a.
)? #? quan tâm đến các số nguyên có dạng a
| \. \ d + # phần định trị có dạng .b
)
([eE] [+ -]? \ d +)? # cuối cùng, tùy chọn khớp với một số mũ
$ / x;

hoặc được viết ở dạng nhỏ gọn,

/ ^ [+ -]? \ * (\ d + (\. \ d *)? | \. \ d +) ([eE] [+ -]? \ d +)? $ /;

Đây là regexp cuối cùng của chúng tôi. Tóm lại, chúng tôi đã xây dựng một regexp bằng cách

· Xác định nhiệm vụ một cách chi tiết,

· Chia nhỏ vấn đề thành các phần nhỏ hơn,

· Dịch các phần nhỏ thành regexps,

· Kết hợp các regexps,

· Và tối ưu hóa regexp kết hợp cuối cùng.

Đây cũng là các bước điển hình liên quan đến việc viết một chương trình máy tính. Điều này làm cho
hoàn hảo, bởi vì biểu thức chính quy về cơ bản là các chương trình được viết bằng một chút
ngôn ngữ máy tính chỉ định các mẫu.

Sử dụng đều đặn biểu thức in Perl
Chủ đề cuối cùng của Phần 1 trình bày ngắn gọn cách regexps được sử dụng trong các chương trình Perl. Nơi làm
chúng phù hợp với cú pháp Perl?

Chúng tôi đã giới thiệu toán tử đối sánh trong mặc định "/ regexp /" và tùy ý
dấu phân cách "m! regexp!" các hình thức. Chúng tôi đã sử dụng toán tử ràng buộc "= ~" và phủ định của nó "! ~"
để kiểm tra các trận đấu chuỗi. Được liên kết với toán tử đối sánh, chúng tôi đã thảo luận về
một dòng "// s", nhiều dòng "// m", "// i" không phân biệt chữ hoa chữ thường và các bổ ngữ "// x" mở rộng.
Có một số điều nữa bạn có thể muốn biết về các toán tử đối sánh.

Cấm thay thế

Nếu bạn thay đổi $ pattern sau khi lần thay thế đầu tiên xảy ra, Perl sẽ bỏ qua nó. nếu bạn
hoàn toàn không muốn bất kỳ sự thay thế nào, hãy sử dụng dấu phân tách đặc biệt "m ''":

@pattern = ('Seuss');
trong khi (<>) {
in if m '@ pattern'; # khớp với nghĩa đen '@pattern', không phải 'Seuss'
}

Tương tự như chuỗi, "m ''" hoạt động giống như dấu nháy đơn trên regexp; tất cả các dấu phân cách "m" khác đều hoạt động
như dấu ngoặc kép. Nếu regexp đánh giá là chuỗi trống, thì regexp trong cuối cùng
thành công phù hợp với được sử dụng thay thế. Vì vậy chúng tôi có

"con chó" = ~ / d /; kết quả # 'd'
"dogbert = ~ //; # điều này khớp với 'd' regexp được sử dụng trước đây

Toàn cầu phù hợp

Hai bổ ngữ cuối cùng mà chúng ta sẽ thảo luận ở đây, "// g" và "// c", liên quan đến nhiều kết quả trùng khớp.
Công cụ sửa đổi "// g" là viết tắt của đối sánh toàn cục và cho phép toán tử đối sánh đối sánh
trong một chuỗi nhiều lần nhất có thể. Trong ngữ cảnh vô hướng, các lệnh gọi liên tiếp
chống lại một chuỗi sẽ có "// g" nhảy từ trận này sang trận khác, theo dõi vị trí trong
chuỗi khi nó đi cùng. Bạn có thể lấy hoặc đặt vị trí bằng hàm "pos ()".

Việc sử dụng "// g" được hiển thị trong ví dụ sau. Giả sử chúng ta có một chuỗi
bao gồm các từ cách nhau bởi dấu cách. Nếu chúng ta biết trước có bao nhiêu từ, chúng ta
có thể trích xuất các từ bằng cách sử dụng các nhóm:

$ x = "nhà cho chó mèo"; # 3 từ
$ x = ~ / ^ \ s * (\ w +) \ s + (\ w +) \ s + (\ w +) \ s * $ /; # diêm,
# $ 1 = 'con mèo'
# $ 2 = 'con chó'
# $ 3 = 'nhà'

Nhưng điều gì sẽ xảy ra nếu chúng ta có một số lượng từ không xác định? Đây là loại nhiệm vụ "// g" là
làm cho. Để trích xuất tất cả các từ, hãy tạo regexp đơn giản "(\ w +)" và lặp qua tất cả các kết quả phù hợp
với "/ (\ w +) / g":

trong khi ($ x = ~ / (\ w +) / g) {
print "Word là $ 1, kết thúc tại vị trí", pos $ x, "\ n";
}

in

Từ là mèo, kết thúc ở vị trí 3
Word is dog, kết thúc ở vị trí 7
Word is house, kết thúc ở vị trí 13

Kết hợp không thành công hoặc thay đổi chuỗi mục tiêu sẽ đặt lại vị trí. Nếu bạn không muốn
đặt lại vị trí sau khi không khớp, thêm "// c", như trong "/ regexp / gc". Hiện tại
vị trí trong chuỗi được liên kết với chuỗi, không phải regexp. Điều này có nghĩa rằng
các chuỗi khác nhau có các vị trí khác nhau và các vị trí tương ứng của chúng có thể được đặt hoặc
đọc độc lập.

Trong ngữ cảnh danh sách, "// g" trả về danh sách các nhóm được so khớp hoặc nếu không có nhóm nào,
danh sách các kết quả phù hợp với toàn bộ regexp. Vì vậy, nếu chúng tôi chỉ muốn các từ, chúng tôi có thể sử dụng

@words = ($ x = ~ / (\ w +) / g); # diêm,
# $ words [0] = 'cat'
# $ words [1] = 'dog'
# $ words [2] = 'nhà'

Được liên kết chặt chẽ với công cụ sửa đổi "// g" là liên kết "\ G". Ký tự liên kết "\ G" phù hợp với
điểm mà kết quả khớp "// g" trước đó bị dừng lại. "\ G" cho phép chúng tôi dễ dàng thực hiện theo ngữ cảnh-
đối sánh nhạy cảm:

$ metric = 1; # sử dụng đơn vị chỉ số
...
$ x = ; # đọc trong phép đo
$ x = ~ / ^ ([+ -]? \ d +) \ s * / g; # nhận được độ lớn
$ trọng lượng = $ 1;
if ($ metric) {# kiểm tra lỗi
in "Lỗi đơn vị!" trừ khi $ x = ~ /\Gkg\./g;
}
khác {
in "Lỗi đơn vị!" trừ khi $ x = ~ /\Glbs\./g;
}
$ x = ~ / \ G \ s + (tiện ích | đĩa xích) / g; # tiếp tục xử lý

Sự kết hợp của "// g" và "\ G" cho phép chúng tôi xử lý chuỗi một chút và sử dụng
logic Perl tùy ý để quyết định phải làm gì tiếp theo. Hiện tại, neo "\ G" chỉ hoàn toàn
được hỗ trợ khi được sử dụng để neo vào phần đầu của mẫu.

"\ G" cũng vô giá trong việc xử lý các bản ghi có độ dài cố định với regexps. Giả sử chúng ta có
một đoạn mã DNA vùng mã hóa, được mã hóa dưới dạng các ký tự cặp cơ sở "ATCGTTGAAT ..." và chúng tôi muốn
để tìm tất cả các mã dừng "TGA". Trong vùng mã hóa, codon là chuỗi 3 chữ cái, vì vậy
chúng ta có thể coi đoạn DNA như một chuỗi các bản ghi gồm 3 chữ cái. Regexp ngây thơ

# mở rộng, đây là "ATC GTT GAA TGC AAA TGA CAT GAC"
$ dna = "ATCGTTGAATGCAAATGACATGAC";
$ dna = ~ / TGA /;

không hoạt động; nó có thể khớp với "TGA", nhưng không có gì đảm bảo rằng đối sánh được căn chỉnh
với các ranh giới codon, ví dụ: chuỗi con "GTT GAA" cho kết quả khớp. Một giải pháp tốt hơn là

while ($ dna = ~ / (\ w \ w \ w) *? TGA / g) {# lưu ý giá trị nhỏ nhất *?
print "Có codon dừng TGA tại vị trí", pos $ dna, "\ n";
}

cái nào in

Có mã dừng TGA ở vị trí 18
Có mã dừng TGA ở vị trí 23

Vị trí 18 là tốt, nhưng vị trí 23 là không có thật. Chuyện gì đã xảy ra thế?

Câu trả lời là regexp của chúng tôi hoạt động tốt cho đến khi chúng tôi vượt qua trận đấu thực cuối cùng. Sau đó
regexp sẽ không khớp với "TGA" được đồng bộ hóa và bắt đầu bước trước một ký tự
vị trí tại một thời điểm, không phải những gì chúng ta muốn. Giải pháp là sử dụng "\ G" để cố định kết quả phù hợp với
căn chỉnh codon:

trong khi ($ dna = ~ / \ G (\ w \ w \ w) *? TGA / g) {
print "Có codon dừng TGA tại vị trí", pos $ dna, "\ n";
}

Bản in này

Có mã dừng TGA ở vị trí 18

Cái nào là câu trả lời đúng. Ví dụ này minh họa rằng điều quan trọng là không chỉ
phù hợp với những gì được mong muốn, nhưng để từ chối những gì không được mong muốn.

(Có các công cụ sửa đổi regexp khác, chẳng hạn như "// o", nhưng chúng chuyên biệt
sử dụng nằm ngoài phạm vi của phần giới thiệu này. )

Tìm kiếm thay thế

Biểu thức chính quy cũng đóng một vai trò quan trọng trong Tìm kiếm thay thế hoạt động trong Perl. Tìm kiếm
và thay thế được thực hiện bằng toán tử "s ///". Hình thức chung là
"s / regexp / Replace / modifiers", với mọi thứ chúng ta biết về regexps và modifier
áp dụng trong trường hợp này. "Thay thế" là một chuỗi được trích dẫn kép Perl
thay thế trong chuỗi bất cứ thứ gì được so khớp với "regexp". Toán tử "= ~" cũng là
được sử dụng ở đây để liên kết một chuỗi với "s ///". Nếu khớp với $ _, "$ _ = ~" có thể là
giảm. Nếu trùng khớp, "s ///" trả về số lần thay thế được thực hiện; nếu không thì
nó trả về false. Đây là vài ví dụ:

$ x = "Đã đến lúc cho mèo ăn!";
$ x = ~ s / cat / hacker /; # $ x chứa "Đã đến lúc nuôi tin tặc!"
if ($ x = ~ s / ^ (Time. * hacker)! $ / $ 1 ngay bây giờ! /) {
$ more_insisting = 1;
}
$ y = "'các từ được trích dẫn'";
$ y = ~ s /^'(.*)'$/$ 1 /; # dải dấu ngoặc kép,
# $ y chứa "các từ được trích dẫn"

Trong ví dụ cuối cùng, toàn bộ chuỗi được so khớp, nhưng chỉ phần bên trong chuỗi đơn
dấu ngoặc kép đã được nhóm lại. Với toán tử "s ///", các biến được so khớp $ 1, $ 2, v.v. là
ngay lập tức có sẵn để sử dụng trong biểu thức thay thế, vì vậy chúng tôi sử dụng $ 1 để thay thế
chuỗi được trích dẫn chỉ với những gì đã được trích dẫn. Với công cụ sửa đổi toàn cục, "s /// g" sẽ tìm kiếm
và thay thế tất cả các lần xuất hiện của regexp trong chuỗi:

$ x = "Tôi đánh 4 ăn 4";
$ x = ~ s / 4/XNUMX /; # không làm được tất cả:
# $ x chứa "Tôi đánh bốn thắng 4"
$ x = "Tôi đánh 4 ăn 4";
$ x = ~ s / 4/XNUMX / g; # hiện tất cả:
# $ x chứa "Tôi đánh bốn thắng bốn"

Nếu bạn thích 'regex' hơn 'regexp' trong hướng dẫn này, bạn có thể sử dụng chương trình sau
để thay thế nó:

% cat> simple_replace
#!/ usr / bin / perl
$ regexp = shift;
$ thay thế = ca;
trong khi (<>) {
s / $ regexp / $ thay thế / g;
in;
}
^D

% simple_replace regrec regex perlretut.pod

Trong "simple_replace", chúng tôi đã sử dụng công cụ sửa đổi "s /// g" để thay thế tất cả các lần xuất hiện của regexp
trên mỗi dòng. (Mặc dù biểu thức chính quy xuất hiện trong một vòng lặp, Perl đủ thông minh
để biên dịch nó chỉ một lần.) Như với "simple_grep", cả "print" và
"s / $ regexp / $ Replace / g" sử dụng ngầm định $ _.

Nếu bạn không muốn "s ///" thay đổi biến ban đầu của mình, bạn có thể sử dụng hàm không phá hủy
bổ ngữ thay thế, "s /// r". Điều này thay đổi hành vi để "s /// r" trả về giá trị cuối cùng
chuỗi được thay thế (thay vì số lần thay thế):

$ x = "Tôi thích chó.";
$ y = $ x = ~ s / chó / mèo / r;
in "$ x $ y \ n";

Ví dụ đó sẽ in "Tôi thích chó. Tôi thích mèo". Lưu ý rằng biến $ x ban đầu có
không bị ảnh hưởng. Thay vào đó, kết quả tổng thể của sự thay thế được lưu trữ trong $ y. Nếu
thay thế không ảnh hưởng đến bất cứ điều gì sau đó chuỗi ban đầu được trả về:

$ x = "Tôi thích chó.";
$ y = $ x = ~ s / voi / báo sư tử / r;
in "$ x $ y \ n"; # in "Tôi thích chó. Tôi thích chó."

Một điều thú vị khác mà cờ "s /// r" cho phép là thay thế chuỗi:

$ x = "Mèo thật tuyệt.";
print $ x = ~ s / Cats / Dogs / r = ~ s / Dogs / Frogs / r = ~
s / Ếch / Nhím / r, "\ n";
# bản in "Nhím thật tuyệt."

Một công cụ sửa đổi có sẵn cụ thể để tìm kiếm và thay thế là đánh giá "s /// e"
bổ nghĩa. "s /// e" coi văn bản thay thế là mã Perl, chứ không phải là dấu ngoặc kép
chuỗi. Giá trị mà mã trả về được thay thế cho chuỗi con phù hợp.
"s /// e" rất hữu ích nếu bạn cần thực hiện một chút tính toán trong quá trình thay thế văn bản.
Ví dụ này đếm tần số ký tự trong một dòng:

$ x = "Tính tiền cho con mèo";
$ x = ~ s /(.)/$ ký tự {$ 1} ++; $ 1 / ví dụ; # cuối cùng $ 1 thay thế char bằng chính nó
print "tần suất của '$ _' là $ chars {$ _} \ n"
foreach (sắp xếp {$ chars {$ b} <=> $ chars {$ a}} key% chars);

Bản in này

tần số của '' là 2
tần số của 't' là 2
tần số của 'l' là 2
tần số của 'B' là 1
tần số của 'c' là 1
tần số của 'e' là 1
tần số của 'h' là 1
tần số của 'i' là 1
tần số của 'a' là 1

Như với toán tử so khớp "m //", "s ///" có thể sử dụng các dấu phân tách khác, chẳng hạn như "s !!!" và
"s {} {}" và thậm chí "s {} //". Nếu các dấu ngoặc kép được sử dụng "s ''", thì regexp và
sự thay thế được coi là chuỗi được trích dẫn đơn và không có sự thay thế biến đổi.
"s ///" trong ngữ cảnh danh sách trả về điều tương tự như trong ngữ cảnh vô hướng, tức là số
diêm.

chia chức năng

Hàm "split ()" là một nơi khác sử dụng regexp. "split / regexp /, string,
giới hạn "tách toán hạng" chuỗi "thành danh sách các chuỗi con và trả về danh sách đó.
Regexp phải được thiết kế để phù hợp với bất kỳ thứ gì cấu thành các dải phân cách cho mong muốn
chuỗi con. "Giới hạn", nếu có, hạn chế tách thành không nhiều hơn "giới hạn"
số lượng chuỗi. Ví dụ: để chia một chuỗi thành các từ, hãy sử dụng

$ x = "Calvin và Hobbes";
@words = split / \ s + /, $ x; # $ word [0] = 'Calvin'
# $ word [1] = 'và'
# $ word [2] = 'Hobbes'

Nếu regexp "//" trống được sử dụng, thì regexp luôn khớp và chuỗi được chia thành
các ký tự riêng lẻ. Nếu regexp có các nhóm, thì danh sách kết quả chứa
các chuỗi con được so khớp từ các nhóm. Ví dụ,

$ x = "/ usr / bin / perl";
@dirs = chia m! / !, $ x; # $ dirs [0] = ''
# $ dirs [1] = 'usr'
# $ dirs [2] = 'bin'
# $ dirs [3] = 'perl'
@parts = split m! (/) !, $ x; # $ phần [0] = ''
# $ phần [1] = '/'
# $ phần [2] = 'usr'
# $ phần [3] = '/'
# $ phần [4] = 'bin'
# $ phần [5] = '/'
# $ phần [6] = 'perl'

Vì ký tự đầu tiên của $ x khớp với regexp, "split" đã thêm vào trước một ký tự đầu trống
phần tử của danh sách.

Nếu bạn đã đọc đến đây, xin chúc mừng! Bây giờ bạn có tất cả các công cụ cơ bản cần thiết để sử dụng
biểu thức chính quy để giải quyết một loạt các vấn đề xử lý văn bản. Nếu đây là của bạn
lần đầu tiên thông qua hướng dẫn, tại sao không dừng lại ở đây và chơi với regexps a
trong khi .... Phần 2 liên quan đến các khía cạnh bí mật hơn của biểu thức chính quy và những
các khái niệm chắc chắn không cần thiết ngay từ đầu.

Phần 2: Power công cụ


OK, bạn biết những điều cơ bản về regexps và bạn muốn biết thêm. Nếu khớp với thông thường
các biểu thức tương tự như đi bộ trong rừng, sau đó các công cụ được thảo luận trong Phần 1 là
tương tự như bản đồ topo và la bàn, những công cụ cơ bản mà chúng tôi sử dụng mọi lúc. Hầu hết các công cụ
trong phần 2 tương tự như súng bắn pháo sáng và điện thoại vệ tinh. Chúng không được sử dụng quá thường xuyên vào
tăng vọt, nhưng khi chúng ta gặp khó khăn, chúng có thể là vô giá.

Sau đây là những khả năng tiên tiến hơn, ít được sử dụng hơn hoặc đôi khi là bí truyền của Perl
regexps. Trong Phần 2, chúng tôi sẽ cho rằng bạn cảm thấy thoải mái với những kiến ​​thức cơ bản và tập trung vào
các tính năng nâng cao.

Xem thêm on nhân vật, dây, tính cách các lớp học
Có một số chuỗi thoát và lớp nhân vật mà chúng tôi chưa đề cập đến.

Có một số chuỗi thoát chuyển đổi các ký tự hoặc chuỗi giữa ký tự trên và
chữ thường và chúng cũng có sẵn trong các mẫu. "\ l" và "\ u" chuyển đổi tiếp theo
ký tự thành chữ thường hoặc chữ hoa, tương ứng:

$ x = "perl";
$ string = ~ / \ u $ x /; # khớp với 'Perl' trong $ string
$ x = "M (rs? | s) \\."; # lưu ý dấu gạch chéo ngược kép
$ string = ~ / \ l $ x /; # khớp với 'mr.', 'mrs.' và 'ms.',

"\ L" hoặc "\ U" biểu thị một sự chuyển đổi lâu dài về chữ hoa, cho đến khi kết thúc bằng "\ E" hoặc được ném
qua một "\ U" hoặc "\ L" khác:

$ x = "Từ này viết thường: \ L SHOUT \ E";
$ x = ~ / la /; # diêm
$ x = "TÔI VẪN BÀN PHÍM THẺ CHO 360 CỦA TÔI"
$ x = ~ / \ Ukeypunch /; # khớp với chuỗi thẻ đục lỗ

Nếu không có "\ E", trường hợp được chuyển đổi cho đến cuối chuỗi. Các regexps
"\ L \ u $ word" hoặc "\ u \ L $ word" chuyển đổi ký tự đầu tiên của $ word thành chữ hoa và phần còn lại
của các ký tự thành chữ thường.

Các ký tự điều khiển có thể được thoát bằng "\ c", do đó ký tự điều khiển-Z sẽ là
khớp với "\ cZ". Chuỗi thoát "\ Q" ... "\ E" trích dẫn hoặc bảo vệ hầu hết các
Ký tự chữ cái. Ví dụ,

$ x = "\ QThat! ^ * &% ~ & cat!";
$ x = ~ / \ Q! ^ * &% ~ & \ E /; # kiểm tra ngôn ngữ thô

Nó không bảo vệ "$" hoặc "@", do đó các biến vẫn có thể được thay thế.

"\ Q", "\ L", "\ l", "\ U", "\ u" và "\ E" thực sự là một phần của cú pháp song ngữ, chứ không phải
một phần của cú pháp regexp thích hợp. Chúng sẽ hoạt động nếu chúng xuất hiện trong một biểu thức chính quy
được nhúng trực tiếp vào một chương trình, nhưng không được nhúng trong một chuỗi được nội suy trong
một mô hình.

Perl regexps có thể xử lý nhiều hơn chỉ bộ ký tự ASCII tiêu chuẩn. Perl hỗ trợ
Unicode, một tiêu chuẩn để đại diện cho các bảng chữ cái từ hầu hết mọi nơi trên thế giới
ngôn ngữ viết và một loạt các ký hiệu. Chuỗi văn bản của Perl là chuỗi Unicode, vì vậy
chúng có thể chứa các ký tự có giá trị (điểm mã hoặc số ký tự) cao hơn 255.

Điều này có ý nghĩa gì đối với regexps? Chà, người dùng regexp không cần biết nhiều về Perl's
biểu diễn bên trong của chuỗi. Nhưng họ cần biết 1) cách biểu diễn Unicode
các ký tự trong một regexp và 2) mà một phép toán so khớp sẽ coi chuỗi là
được tìm kiếm dưới dạng một chuỗi ký tự, không phải byte. Câu trả lời cho 1) là Unicode
ký tự lớn hơn "chr(255) "được biểu diễn bằng ký hiệu" \ x {hex} ", bởi vì
\ x hex (không có dấu ngoặc nhọn) không vượt quá 255. (Bắt đầu từ Perl 5.14, nếu
bạn là người hâm mộ hệ bát phân, bạn cũng có thể sử dụng "\ o {oct}".)

/ \ x {263a} /; # khớp với mặt cười Unicode :)

LƯU Ý: Trong Perl 5.6.0, người ta cần nói "sử dụng utf8" để sử dụng bất kỳ mã Unicode nào
Tính năng, đặc điểm. Đây không còn là trường hợp nữa: đối với hầu hết tất cả các quá trình xử lý Unicode,
pragma "utf8" là không cần thiết. (Trường hợp duy nhất quan trọng là nếu tập lệnh Perl của bạn ở trong
Unicode và được mã hóa bằng UTF-8, khi đó cần có "use utf8" rõ ràng.)

Tìm ra chuỗi thập lục phân của một ký tự Unicode bạn muốn hoặc đang giải mã
Regexp Unicode thập lục phân của người khác cũng thú vị như lập trình trong máy
mã số. Vì vậy, một cách khác để chỉ định các ký tự Unicode là sử dụng tên tính cách thoát
chuỗi "\ N {Tên}". tên là tên cho ký tự Unicode, như được chỉ định trong
Chuẩn Unicode. Ví dụ: nếu chúng tôi muốn đại diện hoặc khớp với dấu hiệu chiêm tinh
đối với hành tinh Mercury, chúng ta có thể sử dụng

$ x = "abc \ N {MERCURY} def";
$ x = ~ / \ N {MERCURY} /; # diêm

Người ta cũng có thể sử dụng tên "ngắn":

print "\ N {GREEK SMALL LETTER SIGMA} được gọi là sigma. \ n";
print "\ N {greek: Sigma} là sigma viết hoa. \ n";

Bạn cũng có thể hạn chế tên trong một bảng chữ cái nhất định bằng cách chỉ định các charnames pragma:

sử dụng charnames qw (Hy Lạp);
print "\ N {sigma} là tiếng Hy Lạp sigma \ n";

Chỉ mục tên ký tự có sẵn trực tuyến từ Unicode Consortium,
<http://www.unicode.org/charts/charindex.html>; tài liệu giải thích với các liên kết đến
tài nguyên tạihttp://www.unicode.org/standard/where>.

Câu trả lời cho yêu cầu 2) là regexp (hầu hết) sử dụng các ký tự Unicode. Các
"chủ yếu" là vì lý do tương thích ngược lộn xộn, nhưng bắt đầu từ Perl 5.14, bất kỳ regex nào
được biên dịch trong phạm vi của một "tính năng sử dụng 'unicode_strings'" (được tự động biến
trong phạm vi "sử dụng 5.012" hoặc cao hơn) sẽ biến "hầu hết" thành "luôn luôn". Nếu như
bạn muốn xử lý Unicode đúng cách, bạn nên đảm bảo rằng 'unicode_strings' đã được chuyển
trên. Bên trong, điều này được mã hóa thành byte bằng cách sử dụng UTF-8 hoặc mã hóa 8 bit nguyên bản,
tùy thuộc vào lịch sử của chuỗi, nhưng về mặt khái niệm thì nó là một chuỗi các ký tự,
không phải byte. Xem perlunitut để biết hướng dẫn về điều đó.

Bây giờ chúng ta hãy thảo luận về các lớp ký tự Unicode, hầu hết thường được gọi là "thuộc tính ký tự".
Chúng được đại diện bởi chuỗi thoát "\ p {name}". Liên kết chặt chẽ là
Thuộc tính "\ P {name}", là sự phủ định của thuộc tính "\ p {name}". Ví dụ, để phù hợp
ký tự viết thường và viết hoa,

$ x = "BOB";
$ x = ~ / ^ \ p {IsUpper} /; # trận đấu, lớp ký tự viết hoa
$ x = ~ / ^ \ P {IsUpper} /; # không khớp, lớp char sans viết hoa
$ x = ~ / ^ \ p {IsLower} /; # không khớp, lớp ký tự viết thường
$ x = ~ / ^ \ P {IsLower} /; # trận đấu, chữ thường sans class char

("Là" là tùy chọn.)

Có rất nhiều thuộc tính ký tự Unicode. Để biết danh sách đầy đủ, hãy xem perluniprops.
Hầu hết chúng đều có từ đồng nghĩa với tên ngắn hơn, cũng được liệt kê ở đó. Một số từ đồng nghĩa là
ký tự đơn. Đối với những điều này, bạn có thể thả mắc cài. Ví dụ: "\ pM" giống nhau
là "\ p {Mark}", nghĩa là những thứ như dấu trọng âm.

Thuộc tính Unicode "\ p {Script}" được sử dụng để phân loại mọi ký tự Unicode thành
tập lệnh ngôn ngữ mà nó được viết. Ví dụ: tiếng Anh, tiếng Pháp và nhiều ngôn ngữ khác
Các ngôn ngữ châu Âu được viết bằng hệ thống chữ Latinh. Nhưng cũng có chữ viết Hy Lạp,
chữ viết Thái Lan, chữ viết Katakana, v.v. Bạn có thể kiểm tra xem một ký tự có trong
tập lệnh cụ thể với, ví dụ: "\ p {Latin}", "\ p {Greek}" hoặc "\ p {Katakana}". Để kiểm tra
nếu nó không có trong tập lệnh Bali, bạn sẽ sử dụng "\ P {Balinese}".

Những gì chúng tôi đã mô tả cho đến nay là dạng đơn của các lớp ký tự "\ p {...}".
Ngoài ra còn có một dạng phức hợp mà bạn có thể gặp phải. Những thứ này trông giống như "\ p {name = value}" hoặc
"\ p {name: value}" (dấu bằng và dấu hai chấm có thể được sử dụng thay thế cho nhau). Đây là những thứ khác
tổng quát hơn dạng đơn và trên thực tế hầu hết các dạng đơn chỉ là do Perl định nghĩa
các phím tắt cho các dạng phức hợp phổ biến. Ví dụ, các ví dụ về tập lệnh trong phần trước
đoạn văn có thể được viết tương đương là "\ p {Script = Latin}", "\ p {Script: Greek}",
"\ p {script = katakana}" và "\ P {script = balinese}" (trường hợp không liên quan giữa "{}"
niềng răng). Bạn có thể không bao giờ phải sử dụng các dạng phức hợp, nhưng đôi khi nó là cần thiết, và
việc sử dụng chúng có thể làm cho mã của bạn dễ hiểu hơn.

"\ X" là chữ viết tắt của một lớp ký tự bao gồm Unicode gia tăng biểu đồ
cụm. Điều này đại diện cho một "ký tự lôgic": những gì dường như là một ký tự đơn lẻ,
nhưng có thể được đại diện nội bộ bởi nhiều hơn một. Ví dụ, sử dụng Unicode đầy đủ
tên, ví dụ: "A + COMBINING RING" là một cụm grapheme với ký tự cơ sở "A" và
kết hợp ký tự "COMBINING RING", dịch từ tiếng Đan Mạch sang A với vòng tròn ở trên cùng
nó, như trong từ Aangstrom.

Để biết thông tin đầy đủ và mới nhất về Unicode, hãy xem tiêu chuẩn Unicode mới nhất, hoặc
Trang web của Unicode Consortiumhttp://www.unicode.org>

Như thể tất cả các lớp đó là không đủ, Perl cũng định nghĩa các lớp ký tự kiểu POSIX.
Chúng có dạng "[: name:]", với "name" là tên của lớp POSIX. POSIX
các lớp là "alpha", "alnum", "ascii", "cntrl", "digit", "graph", "low", "print",
"dấu chấm", "dấu cách", "trên" và "xdigit", và hai phần mở rộng, "từ" (một phần mở rộng Perl cho
khớp "\ w") và "trống" (một phần mở rộng GNU). Bổ ngữ "// a" hạn chế những điều này thành
chỉ khớp trong phạm vi ASCII; nếu không chúng có thể khớp giống với
Các lớp Perl Unicode: "[: upper:]" giống như "\ p {IsUpper}", v.v. (Có một số
các trường hợp ngoại lệ và các vấn đề liên quan đến điều này; xem perlrecharclass để có một cuộc thảo luận đầy đủ.)
"[: digit:]", "[: word:]" và "[: space:]" tương ứng với "\ d", "\ w" và "\ s" quen thuộc
các lớp nhân vật. Để phủ định một lớp POSIX, hãy đặt dấu "^" trước tên, sao cho
ví dụ: "[: ^ chữ số:]" tương ứng với "\ D" và trong Unicode, "\ P {IsDigit}". Unicode và
Các lớp ký tự POSIX có thể được sử dụng giống như "\ d", ngoại trừ POSIX
các lớp ký tự chỉ có thể được sử dụng bên trong một lớp ký tự:

/ \ s + [abc [: digit:] xyz] \ s * /; # khớp với a, b, c, x, y, z hoặc một chữ số
/ ^ = item \ s [[: digit:]] /; # match '= item',
# theo sau bởi một khoảng trắng và một chữ số
/ \ s + [abc \ p {IsDigit} xyz] \ s + /; # khớp với a, b, c, x, y, z hoặc một chữ số
/ ^ = item \ s \ p {IsDigit} /; # match '= item',
# theo sau bởi một khoảng trắng và một chữ số

Chà! Đó là tất cả phần còn lại của các nhân vật và lớp nhân vật.

Biên dịch tiết kiệm đều đặn biểu thức
Trong Phần 1, chúng tôi đã đề cập rằng Perl biên dịch regexp thành một chuỗi opcodes nhỏ gọn.
Do đó, một regexp đã biên dịch là một cấu trúc dữ liệu có thể được lưu trữ một lần và được sử dụng lại và
lần nữa. Đoạn trích dẫn regexp "qr //" thực hiện chính xác điều đó: "qr / string /" biên dịch "chuỗi" dưới dạng
regexp và biến đổi kết quả thành một dạng có thể được gán cho một biến:

$ reg = qr / foo + bar? /; # reg chứa một regexp đã biên dịch

Sau đó, $ reg có thể được sử dụng như một regexp:

$ x = "fooooba";
$ x = ~ $ reg; # trận đấu, giống như / foo + bar? /
$ x = ~ / $ reg /; # điều tương tự, biểu mẫu thay thế

$ reg cũng có thể được nội suy thành một regexp lớn hơn:

$ x = ~ / (abc)? $ reg /; # vẫn khớp

Giống như với toán tử so khớp, dấu ngoặc kép regexp có thể sử dụng các dấu phân cách khác nhau, ví dụ:
"qr !!", "qr {}" hoặc "qr ~~". Dấu nháy đơn làm dấu phân cách ("qr ''") ngăn cản bất kỳ phép nội suy nào.

Các regexps được biên dịch trước rất hữu ích để tạo các đối sánh động không cần
được biên dịch lại mỗi khi chúng gặp phải. Sử dụng regexps được biên dịch trước, chúng tôi viết
chương trình "grep_step" tìm một chuỗi các mẫu, chuyển sang mẫu tiếp theo
ngay sau khi một trong những đã được hài lòng.

% cat> grep_step
#!/ usr / bin / perl
# grep_step - phù hợp regexps, cái này đến cái kia
# sử dụng: multi_grep regexp1 regexp2 ... file1 file2 ...

$ number = ca;
$ regexp [$ _] = shift foreach (0 .. $ number-1);
@compiled = bản đồ qr / $ _ /, @regexp;
trong khi ($ line = <>) {
if ($ line = ~ / $ biên dịch [0] /) {
in dòng $;
shift @ biên dịch;
cuối cùng trừ khi @ biên dịch;
}
}
^D

% grep_step 3 shift in grep_step cuối cùng
$ number = ca;
in dòng $;
cuối cùng trừ khi @ biên dịch;

Việc lưu trữ các regexps được biên dịch trước trong một mảng @compiled cho phép chúng ta chỉ cần lặp lại
regexps mà không cần biên dịch lại, do đó có được tính linh hoạt mà không bị giảm tốc độ.

Sáng tác đều đặn biểu thức at thời gian chạy
Backtracking hiệu quả hơn so với các lần thử lặp lại với các cụm từ thông dụng khác nhau. Nếu như
có một số biểu thức chính quy và kết hợp với bất kỳ biểu thức nào trong số chúng đều có thể chấp nhận được, khi đó
có thể kết hợp chúng thành một tập hợp các lựa chọn thay thế. Nếu các biểu thức riêng lẻ là
dữ liệu đầu vào, điều này có thể được thực hiện bằng cách lập trình một hoạt động nối. Chúng tôi sẽ khai thác ý tưởng này trong
một phiên bản cải tiến của chương trình "simple_grep": một chương trình phù hợp với nhiều
các mẫu:

% cat> multi_grep
#!/ usr / bin / perl
# multi_grep - khớp với bất kỳ regexps
# sử dụng: multi_grep regexp1 regexp2 ... file1 file2 ...

$ number = ca;
$ regexp [$ _] = shift foreach (0 .. $ number-1);
$ pattern = tham gia '|', @regexp;

trong khi ($ line = <>) {
print $ line if $ line = ~ / $ pattern /;
}
^D

% multi_grep 2 dịch chuyển cho multi_grep
$ number = ca;
$ regexp [$ _] = shift foreach (0 .. $ number-1);

Đôi khi, việc xây dựng một mẫu từ đầu vào điều đó sẽ được phân tích
và sử dụng các giá trị cho phép ở phía bên trái của các phép toán đối sánh. Như một
ví dụ cho tình huống hơi nghịch lý này, hãy giả sử rằng đầu vào của chúng ta chứa
Động từ mệnh lệnh phải khớp với một trong số các động từ mệnh lệnh có sẵn, với
xoắn bổ sung mà các lệnh có thể được viết tắt miễn là chuỗi đã cho là duy nhất.
Chương trình dưới đây trình bày thuật toán cơ bản.

% cat> keymatch
#!/ usr / bin / perl
$ kwds = 'bản in danh sách so sánh';
trong khi ($ cmd = <>) {
$ cmd = ~ s / ^ \ s + | \ s + $ // g; # cắt khoảng trắng đầu và cuối
if ((@matches = $ kwds = ~ / \ b $ cmd \ w * / g) == 1) {
print "lệnh: '@matches' \ n";
} elsif (@matches == 0) {
print "không có lệnh như vậy: '$ cmd' \ n";
} Else {
print "không phải là duy nhất: '$ cmd' (có thể là một trong: @matches) \ n";
}
}
^D

% khớp phím
li
lệnh: 'list'
co
không phải là duy nhất: 'co' (có thể là một trong số: copy so sánh)
máy in
không có lệnh như vậy: 'máy in'

Thay vì cố gắng đối sánh đầu vào với từ khóa, chúng tôi đối sánh tập hợp kết hợp gồm
từ khóa chống lại đầu vào. Thao tác đối sánh mẫu "$ kwds = ~ / \ b ($ cmd \ w *) / g" thực hiện
nhiều thứ cùng một lúc. Nó đảm bảo rằng lệnh đã cho bắt đầu khi
từ khóa bắt đầu ("\ b"). Nó chấp nhận các chữ viết tắt do "\ w *" được thêm vào. Nó cho chúng ta biết
số lượng so khớp ("vô hướng @matches") và tất cả các từ khóa đã thực sự được so khớp.
Bạn khó có thể yêu cầu nhiều hơn.

Nhúng Bình luận bổ ngữ in a đều đặn biểu hiện
Bắt đầu với phần này, chúng ta sẽ thảo luận về tập hợp của Perl về gia tăng mô hình. Kia là
là phần mở rộng cho cú pháp biểu thức chính quy truyền thống cung cấp
các công cụ để đối sánh mẫu. Chúng tôi đã thấy các phần mở rộng ở dạng tối thiểu
so khớp các cấu trúc "??", "*?", "+?", "{n, m}?", và "{n,}?". Hầu hết các tiện ích mở rộng bên dưới
có dạng "(? char ...)", trong đó "char" là một ký tự xác định loại
gia hạn.

Phần mở rộng đầu tiên là một nhận xét được nhúng "(? #Text)". Điều này nhúng một nhận xét vào
biểu thức chính quy mà không ảnh hưởng đến ý nghĩa của nó. Nhận xét không được có bất kỳ kết thúc nào
dấu ngoặc đơn trong văn bản. Một ví dụ là

/ (? # Khớp một số nguyên:) [+ -]? \ D + /;

Phong cách nhận xét này phần lớn đã được thay thế bằng nhận xét thô, dạng tự do
được cho phép với sửa đổi "// x".

Hầu hết các từ bổ nghĩa, chẳng hạn như "// i", "// m", "// s" và "// x" (hoặc bất kỳ kết hợp nào của chúng) có thể
cũng được nhúng vào một regexp bằng cách sử dụng "(? i)", "(? m)", "(? s)" và "(? x)". Ví dụ,

/(?tôi đồng ý/; # đối sánh chữ hoa chữ thường 'có'
/Vâng; # điều tương tự
/ (? x) (# phiên bản dạng tự do của một số nguyên regexp
[+ -]? # khớp với một dấu hiệu tùy chọn
\ d + # khớp với một chuỗi các chữ số
)
/NS;

Các công cụ sửa đổi được nhúng có thể có hai ưu điểm quan trọng so với các công cụ sửa đổi thông thường. Được nhúng
công cụ sửa đổi cho phép một tập hợp các công cụ sửa đổi tùy chỉnh để mỗi mô hình regexp. Điều này là tuyệt vời cho
khớp với một mảng regexps phải có các công cụ sửa đổi khác nhau:

$ pattern [0] = '(? i) bác sĩ';
$ pattern [1] = 'Johnson';
...
trong khi (<>) {
foreach $ patt (@pattern) {
in if / $ patt /;
}
}

Ưu điểm thứ hai là các công cụ sửa đổi được nhúng (ngoại trừ "// p", sửa đổi toàn bộ
regexp) chỉ ảnh hưởng đến regexp bên trong nhóm mà công cụ sửa đổi nhúng được chứa trong đó. Vì vậy
nhóm có thể được sử dụng để bản địa hóa các hiệu ứng của công cụ sửa đổi:

/ Trả lời: ((? I) có) /; # khớp với 'Câu trả lời: có', 'Câu trả lời: CÓ', v.v.

Các công cụ sửa đổi được nhúng cũng có thể tắt bất kỳ công cụ sửa đổi nào đã có bằng cách sử dụng, ví dụ:
"(?-tôi)". Các từ bổ nghĩa cũng có thể được kết hợp thành một biểu thức duy nhất, ví dụ: "(? Si)" bật
chế độ dòng đơn và tắt phân biệt chữ hoa chữ thường.

Các công cụ sửa đổi được nhúng cũng có thể được thêm vào nhóm không chụp. "(? im: regexp)" là một
nhóm không chụp khớp với chữ hoa chữ thường "regexp" và tắt nhiều dòng
chế độ.

Tìm kiếm trước tìm kiếm sau
Phần này liên quan đến các khẳng định về cái nhìn trước và cái nhìn sau. Đầu tiên, một chút
nền.

Trong biểu thức chính quy Perl, hầu hết các phần tử regexp 'ăn hết' một lượng chuỗi nhất định khi
chúng hợp nhau. Ví dụ: phần tử regexp "[abc}]" ăn một ký tự của chuỗi
khi nó khớp, theo nghĩa Perl di chuyển đến vị trí ký tự tiếp theo trong chuỗi
sau trận đấu. Tuy nhiên, có một số yếu tố không ăn nhập các ký tự (nâng cao
vị trí ký tự) nếu chúng khớp. Các ví dụ mà chúng ta đã thấy cho đến nay là các mỏ neo.
Ký tự "^" khớp với đầu dòng, nhưng không ăn bất kỳ ký tự nào.
Tương tự, ký tự liên kết ranh giới từ "\ b" khớp với bất kỳ ký tự nào khớp với "\ w"
bên cạnh một ký tự không có, nhưng nó không ăn bớt bất kỳ ký tự nào. Mỏ neo
là những ví dụ về chiều rộng bằng không khẳng định: zero-width, vì chúng không sử dụng ký tự nào và
xác nhận, bởi vì chúng kiểm tra một số thuộc tính của chuỗi. Trong bối cảnh của chuyến đi bộ của chúng tôi trong
sự tương tự trong rừng với đối sánh regexp, hầu hết các yếu tố regexp di chuyển chúng ta dọc theo một con đường mòn, nhưng
neo cho phép chúng ta dừng lại một chút và kiểm tra môi trường xung quanh. Nếu môi trường địa phương kiểm tra
ra ngoài, chúng ta có thể tiếp tục. Nhưng nếu môi trường địa phương không làm hài lòng chúng tôi, chúng tôi phải
nhạc nền.

Kiểm tra môi trường đòi hỏi phải nhìn về phía trước trên đường mòn, nhìn ra phía sau, hoặc
cả hai. "^" nhìn ra phía sau để thấy rằng không có ký tự nào trước đó. "$" nhìn về phía trước, để
thấy rằng không có ký tự sau. "\ b" nhìn cả phía trước và phía sau để xem liệu
các ký tự ở hai bên khác nhau về "word-ness" của chúng.

Các khẳng định lookahead và lookbehind là những khái quát của khái niệm neo.
Lookahead và lookbehind là các xác nhận không có chiều rộng cho phép chúng tôi chỉ định những ký tự nào chúng tôi
muốn thử nghiệm cho. Xác nhận lookahead được biểu thị bằng "(? = Regexp)" và lookbehind
khẳng định được ký hiệu là "(? <= fixed-regexp)". Một số ví dụ

$ x = "Tôi bắt được housecat 'Tom-cat' bằng catnip";
$ x = ~ / cat (? = \ s) /; # khớp với 'cat' trong 'housecat'
@catwords = ($ x = ~ / (? <= \ s) cat \ w + / g); # diêm,
# $ catwords [0] = 'bắt'
# $ catwords [1] = 'catnip'
$ x = ~ / \ bcat \ b /; # khớp với 'cat' trong 'Tom-cat'
$ x = ~ / (? <= \ s) cat (? = \ s) /; # không khớp; không có 'con mèo' bị cô lập trong
# giữa $ x

Lưu ý rằng các dấu ngoặc đơn trong "(? = Regexp)" và "(? <= Regexp)" không bắt giữ, vì chúng
là các xác nhận có độ rộng bằng không. Do đó, trong regexp thứ hai, các chuỗi con được bắt là những
của toàn bộ regexp của chính nó. Lookahead "(? = Regexp)" có thể khớp với các regexps tùy ý, nhưng
lookbehind "(? <= fixed-regexp)" chỉ hoạt động với các regexps có chiều rộng cố định, tức là một số cố định
dài ký tự. Vì vậy, "(? <= (Ab | bc))" là được, nhưng "(? <= (Ab) *)" thì không. Phủ định
các phiên bản của xác nhận lookahead và lookbehind được biểu thị bằng "(?! regexp)" và
”(? không trận đấu:

$ x = "foobar";
$ x = ~ / foo (?! thanh) /; # không khớp, 'thanh' theo sau 'foo'
$ x = ~ / foo (?! baz) /; # trận đấu, 'baz' không theo sau 'foo'
$ x = ~ / (?

"\ C" không được hỗ trợ trong lookbehind, vì định nghĩa "\ C" vốn đã khó hiểu
thậm chí sẽ trở nên nhiều hơn như vậy khi đi ngược lại.

Đây là một ví dụ trong đó một chuỗi chứa các từ được phân tách bằng ô trống, số và
dấu gạch ngang được chia thành các thành phần của nó. Chỉ sử dụng "/ \ s + /" sẽ không hoạt động vì dấu cách
không bắt buộc phải có giữa dấu gạch ngang, hoặc một từ hoặc một dấu gạch ngang. Các địa điểm bổ sung cho sự phân chia là
được thiết lập bằng cách nhìn về phía trước và phía sau:

$ str = "một hai - --6-8";
@toks = split / \ s + # một loạt dấu cách
| (? <= \ S) (? = -) # bất kỳ không phải dấu cách nào theo sau bởi '-'
| (? <= -) (? = \ S) # a '-' theo sau bởi bất kỳ dấu cách nào
/ x, $ str; # @toks = qw (một hai - - - 6 - 8)

Sử dụng độc lập biểu hiện phụ đến ngăn chặn quay lui
Độc lập biểu hiện phụ là các biểu thức chính quy, trong ngữ cảnh của một
biểu thức, hàm đó độc lập với biểu thức chính quy lớn hơn. Đó là, họ
tiêu thụ nhiều hay ít chuỗi tùy thích mà không quan tâm đến khả năng
regexp lớn hơn để phù hợp. Các biểu thức con độc lập được đại diện bởi "(?> Regexp)".
Chúng ta có thể minh họa hành vi của chúng bằng cách xem xét một regexp thông thường trước tiên:

$ x = "ab";
$ x = ~ / a * ab /; # diêm

Điều này rõ ràng là đối sánh, nhưng trong quá trình đối sánh, biểu thức phụ "a *" trước tiên
nắm lấy "a". Tuy nhiên, làm như vậy sẽ không cho phép toàn bộ regexp khớp, vì vậy sau
backtracking, "a *" cuối cùng trả lại "a" và khớp với chuỗi trống. Đây, cái gì
"a *" được so khớp là phụ thuộc về những gì phần còn lại của regexp khớp.

Đối chiếu điều đó với một biểu thức con độc lập:

$ x = ~ / (?> a *) ab /; # không trùng khớp!

Biểu thức con độc lập "(?> A *)" không quan tâm đến phần còn lại của regexp, vì vậy nó
nhìn thấy một "a" và lấy nó. Khi đó phần còn lại của regexp "ab" không thể khớp. Bởi vì
"(?> a *)" là độc lập, không có backtracking và biểu thức con độc lập không
không từ bỏ "a" của nó. Vì vậy, kết hợp của regexp nói chung không thành công. Một hành vi tương tự
xảy ra với các regexps hoàn toàn độc lập:

$ x = "ab";
$ x = ~ / a * / g; # trận đấu, ăn một 'a'
$ x = ~ / \ Gab / g; # không khớp, không có 'a' nào

Ở đây "// g" và "\ G" tạo một phân phối 'nhóm thẻ' của chuỗi từ một regexp đến
khác. Regexps với một biểu thức con độc lập giống như thế này, với một phần
chuỗi cho biểu thức con độc lập và chuyển chuỗi trở lại
bao quanh regexp.

Khả năng của một biểu thức con độc lập để ngăn chặn việc bẻ khóa ngược có thể khá hữu ích.
Giả sử chúng ta muốn so khớp một chuỗi không rỗng được đặt trong dấu ngoặc đơn sâu đến hai cấp.
Sau đó, các regexp sau phù hợp với:

$ x = "abc (de (fg) h"; # dấu ngoặc đơn không cân đối
$ x = ~ / \ (([^ ()] + | \ ([^ ()] * \)) + \) / x;

Regexp khớp với một dấu ngoặc đơn đang mở, một hoặc nhiều bản sao của sự thay thế và một dấu đóng
dấu ngoặc đơn. Sự thay thế có hai chiều, với sự thay thế đầu tiên "[^ ()] +" phù hợp với một
chuỗi con không có dấu ngoặc đơn và chuỗi thay thế thứ hai "\ ([^ ()] * \)" khớp với một
chuỗi con được phân cách bằng dấu ngoặc đơn. Vấn đề với regexp này là nó
bệnh lý: nó có các định lượng không xác định được lồng vào nhau có dạng "(a + | b) +". Chúng tôi đã thảo luận
trong Phần 1, cách các bộ định lượng lồng nhau như thế này có thể mất một thời gian dài theo cấp số nhân để
thực hiện nếu không có kết quả phù hợp có thể. Để ngăn chặn sự bùng nổ theo cấp số nhân, chúng ta cần
ngăn chặn việc bẻ khóa ngược vô ích tại một số thời điểm. Điều này có thể được thực hiện bằng cách bao bọc bên trong
định lượng như một biểu thức con độc lập:

$ x = ~ / \ (((?> [^ ()] +) | \ ([^ ()] * \)) + \) / x;

Ở đây, "(?> [^ ()] +)" Phá vỡ sự thoái hóa của phân vùng chuỗi bằng cách tiêu thụ càng nhiều
chuỗi càng tốt và giữ nó. Sau đó, các trận đấu thất bại nhanh chóng hơn nhiều.

có điều kiện biểu thức
A có điều kiện biểu hiện là một dạng câu lệnh if-then-else cho phép người ta chọn
những mẫu nào sẽ được phù hợp, dựa trên một số điều kiện. Có hai loại
biểu thức điều kiện: "(? (condition) yes-regexp)" và
"(? (điều kiện) yes-regexp | no-regexp)". "(? (condition) yes-regexp)" giống như một 'if () {}'
tuyên bố trong Perl. Nếu "điều kiện" là đúng, thì "yes-regexp" sẽ được so khớp. Nếu
"condition" là sai, "yes-regexp" sẽ bị bỏ qua và Perl sẽ chuyển sang điều tiếp theo
phần tử regexp. Dạng thứ hai giống như một câu lệnh 'if () {} else {}' trong Perl. Nếu
"điều kiện" là đúng, "yes-regexp" sẽ được so khớp, nếu không, "no-regexp" sẽ là
phù hợp.

"Điều kiện" có thể có nhiều dạng. Dạng đầu tiên chỉ đơn giản là một số nguyên trong
dấu ngoặc "(số nguyên)". Đúng nếu tham chiếu ngược tương ứng "\ integer" phù hợp
trước đó trong regexp. Điều tương tự cũng có thể được thực hiện với một cái tên được liên kết với một ảnh chụp
nhóm, được viết là "( ) "hoặc" ('name') ". Dạng thứ hai là độ rộng bằng XNUMX trống
khẳng định "(? ...)", một cái nhìn trước, một cái nhìn sau hoặc một xác nhận mã (được thảo luận trong
phần tiếp theo). Bộ biểu mẫu thứ ba cung cấp các bài kiểm tra trả về true nếu
biểu thức được thực thi trong một đệ quy ("(R)") hoặc đang được gọi từ một số chụp
nhóm, được tham chiếu theo số ("(R1)", "(R2)", ...) hoặc theo tên ("(R & name)").

Số nguyên hoặc dạng tên của "điều kiện" cho phép chúng tôi chọn, với tính linh hoạt hơn,
những gì cần khớp dựa trên những gì đã khớp trước đó trong regexp. Điều này tìm kiếm các từ của
dạng "$ x $ x" hoặc "$ x $ y $ y $ x":

% simple_grep '^ (\ w +) (\ w +)? (? (2) \ g2 \ g1 | \ g1) $' / usr / dict / words
beriberi
coco
couscous
chứng thư
...
thổi còi
toto
tutu

"Điều kiện" phía sau cho phép, cùng với các tham chiếu ngược, một phần trước của trận đấu
ảnh hưởng đến phần sau của trận đấu. Ví dụ,

/ [ATGC] + (? (? <= AA) G | C) $ /;

khớp với trình tự DNA sao cho nó kết thúc bằng "AAG" hoặc một số cặp cơ sở khác
kết hợp và "C". Lưu ý rằng dạng là "(? (? <= AA) G | C)" chứ không phải "(? ((? <= AA)) G | C)"; vì
xác nhận lookahead, lookbehind hoặc code, các dấu ngoặc đơn xung quanh điều kiện là
không cần thiết.

Xác định tên mô hình
Một số biểu thức chính quy sử dụng các tiêu ngữ con giống hệt nhau ở một số nơi. Bắt đầu với Perl
5.10, có thể xác định các bản chất con được đặt tên trong một phần của mẫu để chúng
có thể được gọi tên ở bất kỳ vị trí nào trong mô hình. Mô hình cú pháp này cho điều này
nhóm định nghĩa là "(? (DEFINE) (? mẫu) ...) ". Việc chèn một mẫu có tên là
được viết là "(? & tên)".

Ví dụ dưới đây minh họa tính năng này bằng cách sử dụng mẫu cho số dấu phẩy động
đã được trình bày trước đó. Ba bài toán con được sử dụng nhiều hơn một lần là
dấu hiệu tùy chọn, dãy chữ số cho một số nguyên và phân số thập phân. ĐỊNH NGHĨA
nhóm ở cuối mẫu chứa định nghĩa của chúng. Chú ý rằng số thập phân
mẫu phân số là nơi đầu tiên chúng ta có thể sử dụng lại mẫu số nguyên.

/ ^ (? & osg) \ * ((? & int) (? & dec)? | (? & dec))
(?: [eE] (? & osg) (? & int))?
$
(?(ĐỊNH NGHĨA)
(? [- +]?) # dấu tùy chọn
(? \ d ++) # số nguyên
(? \. (? & int)) # phân số thập phân
) / x

Đệ quy mô hình
Tính năng này (được giới thiệu trong Perl 5.10) mở rộng đáng kể sức mạnh của mẫu Perl
sự phù hợp. Bằng cách tham chiếu đến một số nhóm chụp khác ở bất kỳ đâu trong mô hình với
cấu trúc "(? group-ref)", Belt Hold trong nhóm được tham chiếu được sử dụng như một
subpattern độc lập thay cho chính tham chiếu nhóm. Vì nhóm
tài liệu tham khảo có thể được chứa ở trong nhóm mà nó đề cập đến, bây giờ có thể áp dụng
khớp mẫu với các tác vụ cho đến nay yêu cầu trình phân tích cú pháp đệ quy.

Để minh họa tính năng này, chúng tôi sẽ thiết kế một mẫu phù hợp nếu một chuỗi chứa
palindrome. (Đây là một từ hoặc một câu, trong khi bỏ qua dấu cách, dấu chấm câu giữa
và trường hợp, đọc ngược tương tự như chuyển tiếp. Chúng tôi bắt đầu bằng cách quan sát rằng trống rỗng
chuỗi hoặc chuỗi chỉ chứa một ký tự từ là palindrome. Nếu không thì nó phải
có một ký tự từ ở phía trước và giống ở cuối, với một palindrome khác ở
giữa.

/ (?: (\ w) (? ... Đây là một palindrome ...) \ g {-1} | \ w?) / x

Thêm "\ W *" vào một trong hai đầu để loại bỏ những gì sẽ bị bỏ qua, chúng tôi đã có đầy đủ
mẫu:

my $ pp = qr / ^ (\ W * (?: (\ w) (? 1) \ g {-1} | \ w?) \ W *) $ / ix;
cho $ s ("saippuakauppias", "Một con người, một kế hoạch, một kênh đào: Panama!") {
print "'$ s' là một palindrome \ n" nếu $ s = ~ / $ pp /;
}

Trong "(? ...)" có thể sử dụng cả tham chiếu ngược tuyệt đối và tương đối. Toàn bộ mẫu có thể
được lắp lại bằng "(? R)" hoặc "(? 0)". Nếu bạn muốn đặt tên cho các nhóm của mình, bạn có thể sử dụng
"(? & name)" để lặp lại vào nhóm đó.

A bit of ma thuật: thi hành Perl in a đều đặn biểu hiện
Thông thường, regexps là một phần của biểu thức Perl. đánh giá biểu thức biến điều đó
xung quanh bằng cách cho phép mã Perl tùy ý trở thành một phần của regexp. Đánh giá mã
biểu thức được ký hiệu là "(? {code})", với một chuỗi câu lệnh Perl.

Hãy cảnh báo rằng tính năng này được coi là thử nghiệm và có thể được thay đổi mà không cần báo trước.

Biểu thức mã là các xác nhận có độ rộng bằng không và giá trị mà chúng trả về phụ thuộc vào
môi trường. Có hai khả năng: hoặc biểu thức mã được sử dụng làm
có điều kiện trong một biểu thức điều kiện "(? (điều kiện) ...)", hoặc không. Nếu mã
biểu thức là một điều kiện, mã được đánh giá và kết quả (tức là, kết quả của
câu lệnh cuối cùng) được sử dụng để xác định sự thật hay giả dối. Nếu biểu thức mã không
được sử dụng như một điều kiện, khẳng định luôn đánh giá đúng và kết quả được đưa vào
biến đặc biệt $ ^ R. Biến $ ^ R sau đó có thể được sử dụng trong các biểu thức mã sau này trong
regexp. Dưới đây là một số ví dụ ngớ ngẩn:

$ x = "abcdef";
$ x = ~ / abc (? {print "Chào Mẹ!";}) def /; # diêm,
# in 'Chào mẹ!'
$ x = ~ / aaa (? {print "Chào Mẹ!";}) def /; # không khớp,
# không 'Chào mẹ!'

Hãy chú ý cẩn thận đến ví dụ tiếp theo:

$ x = ~ / abc (? {print "Chào Mẹ!";}) ddd /; # không khớp,
# không 'Chào mẹ!'
# nhưng tại sao không?

Thoạt nhìn, bạn sẽ nghĩ rằng nó không nên in, bởi vì rõ ràng là "ddd" không
sẽ khớp với chuỗi mục tiêu. Nhưng hãy nhìn vào ví dụ này:

$ x = ~ / abc (? {print "Chào Mẹ!";}) [dD] dd /; # không khớp,
# nhưng _không_ in

Hừ! Điều gì đã xảy ra ở đây? Nếu bạn đã theo dõi, bạn biết rằng mô hình trên
nên có hiệu quả (gần như) giống như cuối cùng; bao quanh "d" trong một ký tự
lớp sẽ không thay đổi những gì nó phù hợp. Vậy tại sao đầu tiên không in trong khi
cái thứ hai không?

Câu trả lời nằm ở sự tối ưu hóa mà công cụ regex thực hiện. Trong trường hợp đầu tiên, tất cả
động cơ thấy là các ký tự cũ thuần túy (ngoài cấu trúc "? {}"). Nó đủ thông minh
để nhận ra rằng chuỗi 'ddd' không xuất hiện trong chuỗi mục tiêu của chúng tôi trước khi thực sự
chạy mô hình thông qua. Nhưng trong trường hợp thứ hai, chúng tôi đã đánh lừa nó khi nghĩ rằng
mô hình của chúng tôi là phức tạp hơn. Nó sẽ xem xét, xem lớp nhân vật của chúng tôi và quyết định
rằng nó sẽ phải thực sự chạy mẫu để xác định xem nó có khớp hay không và
trong quá trình chạy, nó truy cập vào câu lệnh in trước khi nó phát hiện ra rằng chúng tôi không
có một trận đấu.

Để xem xét kỹ hơn cách động cơ thực hiện tối ưu hóa, hãy xem phần "Pragmas và
gỡ lỗi "bên dưới.

Thú vị hơn với "? {}":

$ x = ~ / (? {print "Chào Mẹ!";}) /; # diêm,
# in 'Chào mẹ!'
$ x = ~ / (? {$ c = 1;}) (? {print "$ c";}) /; # diêm,
# bản in '1'
$ x = ~ / (? {$ c = 1;}) (? {print "$ ^ R";}) /; # diêm,
# bản in '1'

Một chút ma thuật được đề cập trong tiêu đề phần xảy ra khi regexp lùi lại trong
quá trình tìm kiếm một trận đấu. Nếu regexp quay lại một biểu thức mã và nếu
các biến được sử dụng bên trong được bản địa hóa bằng cách sử dụng "cục bộ", những thay đổi trong các biến
được tạo ra bởi biểu thức mã được hoàn tác! Do đó, nếu chúng ta muốn đếm bao nhiêu lần
ký tự được khớp trong một nhóm, chúng ta có thể sử dụng, ví dụ:

$ x = "aaaa";
$ count = 0; # khởi tạo số lượng 'a'
$ c = "bob"; # kiểm tra xem $ c có bị che khuất không
$ x = ~ / (? {local $ c = 0;}) # số khởi tạo
(a # so khớp 'a'
(? {local $ c = $ c + 1;}) # số gia tăng
) * # làm điều này bất kỳ số lần nào,
aa # nhưng khớp với 'aa' ở cuối
(? {$ count = $ c;}) # copy local $ c var vào $ count
/NS;
print "a" count là $ count, biến \ $ c là '$ c' \ n ";

Bản in này

số lượng 'a' là 2, biến $ c là 'bob'

Nếu chúng ta thay thế "(? {Local $ c = $ c + 1;})" bằng "(? {$ C = $ c + 1;})", thì biến
thay đổi là không hoàn tác trong quá trình backtracking và chúng tôi nhận được

số lượng 'a' là 4, biến $ c là 'bob'

Lưu ý rằng chỉ những thay đổi biến được bản địa hóa mới được hoàn tác. Các tác dụng phụ khác của mã
thực thi biểu thức là vĩnh viễn. Như vậy

$ x = "aaaa";
$ x = ~ / (a ​​(? {print "Yow \ n";})) * aa /;

sản xuất

yow
yow
yow
yow

Kết quả $ ^ R được bản địa hóa tự động, do đó nó sẽ hoạt động bình thường khi có
của backtracking.

Ví dụ này sử dụng một biểu thức mã trong một điều kiện để khớp với một bài viết xác định,
'the' trong tiếng Anh hoặc 'der | die | das' trong tiếng Đức:

$ lang = 'DE'; # sử dụng tiếng Đức
...
$ text = "das";
in "khớp \ n"
nếu $ text = ~ / (? (? {
$ lang eq 'EN'; # ngôn ngữ có phải là tiếng Anh không?
})
cái | # nếu vậy, hãy đối sánh với 'the'
(der | die | das) # else, so khớp 'der | die | das'
)
/ xi;

Lưu ý rằng cú pháp ở đây là "(? (? {...}) yes-regexp | no-regexp)", không phải
"(? ((? {...})) yes-regexp | no-regexp)". Nói cách khác, trong trường hợp biểu thức mã, chúng ta
không cần dấu ngoặc đơn xung quanh điều kiện.

Nếu bạn cố gắng sử dụng các biểu thức mã trong đó văn bản mã được chứa trong một
thay vì xuất hiện theo nghĩa đen trong mẫu, Perl có thể làm bạn ngạc nhiên:

$ thanh = 5;
$ pat = '(? {1})';
/ foo (? {$ bar}) bar /; # biên dịch ổn, $ bar không được nội suy
/ foo (? {1}) $ bar /; # biên dịch ok, $ bar được nội suy
/ foo $ {pat} bar /; # Lỗi biên dịch!

$ pat = qr / (? {$ foo = 1}) /; # mã biên dịch trước regexp
/ foo $ {pat} bar /; # biên dịch ok

Nếu một regexp có một biến nội suy một biểu thức mã, Perl sẽ coi regexp là
một lỗi. Tuy nhiên, nếu biểu thức mã được biên dịch trước thành một biến, thì nội suy là
Vâng. Câu hỏi đặt ra là tại sao đây lại là một lỗi?

Lý do là nội suy biến và các biểu thức mã cùng nhau tạo ra một bảo mật
rủi ro. Sự kết hợp này rất nguy hiểm vì nhiều lập trình viên viết công cụ tìm kiếm
thường lấy thông tin đầu vào của người dùng và cắm trực tiếp vào regexp:

$ regexp = <>; # read regexp do người dùng cung cấp
$ chomp $ regexp; # loại bỏ dòng mới có thể có
$ text = ~ / $ regexp /; # search $ text cho $ regexp

Nếu biến $ regexp chứa một biểu thức mã, thì người dùng có thể thực thi tùy ý
Mã Perl. Ví dụ: một số joker có thể tìm kiếm "system ('rm -rf *');" để xóa của bạn
các tập tin. Theo nghĩa này, sự kết hợp của biểu thức nội suy và mã vết nhơ qua một vài thao tác đơn giản về
regexp. Vì vậy, theo mặc định, sử dụng cả biểu thức nội suy và mã trong cùng một regexp
không được phép. Nếu bạn không lo lắng về những người dùng độc hại, bạn có thể bỏ qua
kiểm tra bảo mật này bằng cách gọi "use re 'eval'":

sử dụng re 'eval'; # cẩn thận khi ra khỏi cửa
$ thanh = 5;
$ pat = '(? {1})';
/ foo $ {pat} bar /; # biên dịch ok

Một dạng biểu thức mã khác là Belt Hold biểu hiện. Mã mẫu
biểu thức giống như một biểu thức mã thông thường, ngoại trừ kết quả của mã
đánh giá được coi như một biểu thức chính quy và được đối sánh ngay lập tức. Một ví dụ đơn giản
is

$ chiều dài = 5;
$ char = 'a';
$ x = 'aaaaabb';
$ x = ~ / (?? {$ char x $ length}) / x; # trận đấu, có 5 trong số 'a'

Ví dụ cuối cùng này chứa cả biểu thức mã thông thường và mã mẫu. Nó phát hiện
liệu một chuỗi nhị phân 1101010010001 ... có khoảng cách Fibonacci 0,1,1,2,3,5, ... trong số
1 của:

$ x = "1101010010001000001";
$ z0 = ''; $ z1 = '0'; # điều kiện ban đầu
print "Đó là một dãy Fibonacci \ n"
nếu $ x = ~ / ^ 1 # khớp với '1' ban đầu
(?:
((?? {$ z0})) # khớp với một số '0'
1 # và sau đó là '1'
(? {$ z0 = $ z1; $ z1. = $ ^ N;})
) + # lặp lại nếu cần
$ # đó là tất cả những gì có
/NS;
printf "Dãy lớn nhất được so khớp là% d \ n", length ($ z1) -length ($ z0);

Hãy nhớ rằng $ ^ N được đặt thành bất kỳ giá trị nào được khớp với nhóm chụp hoàn thành cuối cùng. Điều này
in

Nó là một dãy Fibonacci
Chuỗi lớn nhất được so khớp là 5

Ha! Hãy thử điều đó với gói regexp đa dạng khu vườn của bạn ...

Lưu ý rằng các biến $ z0 và $ z1 không được thay thế khi regexp được biên dịch, như
xảy ra đối với các biến thông thường bên ngoài một biểu thức mã. Đúng hơn, toàn bộ khối mã là
được phân tích cú pháp dưới dạng mã perl cùng lúc khi perl đang biên dịch mã chứa chữ
mô hình regexp.

Regexp không có công cụ sửa đổi "// x" là

/ ^ 1 (?: ((?? {$ z0})) 1 (? {$ Z0 = $ z1; $ z1. = $ ^ N;})) + $ /

điều này cho thấy rằng vẫn có thể có khoảng trắng trong các phần mã. Tuy nhiên, khi làm việc
với mã và biểu thức điều kiện, dạng mở rộng của regexps gần như cần thiết trong
tạo và gỡ lỗi regexps.

Quay lui điều khiển động từ
Perl 5.10 đã giới thiệu một số động từ điều khiển nhằm cung cấp khả năng kiểm soát chi tiết
quá trình backtracking, bằng cách ảnh hưởng trực tiếp đến động cơ regexp và bằng cách cung cấp
kỹ thuật giám sát. Vì tất cả các tính năng trong nhóm này đều là thử nghiệm và tùy thuộc vào
thay đổi hoặc loại bỏ trong phiên bản tương lai của Perl, người đọc quan tâm được tham khảo
"Động từ điều khiển Backtracking đặc biệt" trong perlre để có mô tả chi tiết.

Dưới đây chỉ là một ví dụ minh họa động từ điều khiển "(* FAIL)", có thể là
viết tắt là "(* F)". Nếu điều này được chèn vào một regexp, nó sẽ khiến nó không thành công, giống như
nó sẽ không khớp giữa mẫu và chuỗi. Xử lý regexp
tiếp tục như nó sẽ xảy ra sau bất kỳ lỗi "bình thường" nào, vì vậy, ví dụ: vị trí tiếp theo
trong chuỗi hoặc thay thế khác sẽ được thử. Vì không phù hợp sẽ không bảo toàn
thu thập các nhóm hoặc tạo ra kết quả, có thể cần sử dụng kết hợp với
mã nhúng.

% đếm = ();
"supercalifragilisticexpialidocious" = ~
/ ([aeiou]) (? {$ count {$ 1} ++;}) (* FAIL) / i;
printf "% 3d '% s' \ n", $ count {$ _}, $ _ for (phím sắp xếp% count);

Mẫu bắt đầu với một lớp khớp với một tập hợp con các chữ cái. Bất cứ khi nào điều này phù hợp,
câu lệnh như "$ count {'a'} ++;" được thực thi, tăng bộ đếm của chữ cái. sau đó
"(* FAIL)" thực hiện những gì nó nói, và công cụ regexp tiến hành theo cuốn sách: miễn là
vì chưa đạt đến cuối chuỗi, vị trí được nâng cao trước khi tìm kiếm
nguyên âm khác. Do đó, khớp hoặc không khớp không tạo ra sự khác biệt và công cụ regexp tiến hành
cho đến khi toàn bộ chuỗi đã được kiểm tra. (Đáng chú ý là một giải pháp thay thế
sử dụng một cái gì đó như

$ count {lc ($ _)} ++ cho split ('', "supercalifragilisticexpialidocious");
printf "% 3d '% s' \ n", $ count2 {$ _}, $ _ for (qw {aeiou});

chậm hơn đáng kể.)

Thực dụng gỡ lỗi
Nói về gỡ lỗi, có một số pragmas có sẵn để kiểm soát và gỡ lỗi các regexps trong
Perl. Chúng ta đã gặp một pragma trong phần trước, "use re 'eval';",
cho phép nội suy biến và các biểu thức mã cùng tồn tại trong một regexp. Cai khac
pragmas là

sử dụng lại 'taint';
$ tainted = <>;
@parts = ($ tainted = ~ / (\ w +) \ s + (\ w +) /; # @parts hiện đã bị nhiễm độc

Pragma "taint" khiến bất kỳ chuỗi con nào từ một trận đấu với một biến bị nhiễm độc trở thành
cũng bị nhiễm độc. Đây không phải là trường hợp bình thường, vì regexps thường được sử dụng để trích xuất
các bit an toàn từ một biến bị nhiễm bẩn. Sử dụng "taint" khi bạn không trích xuất các bit an toàn, nhưng
đang thực hiện một số xử lý khác. Cả pragmas "taint" và "eval" đều là từ vựng
phạm vi, có nghĩa là chúng chỉ có hiệu lực cho đến khi kết thúc khối bao quanh
thực dụng.

sử dụng re '/ m'; # hoặc bất kỳ cờ nào khác
$ multiline_string = ~ / ^ foo /; # / m được ngụ ý

Pragma "re '/ flags'" (được giới thiệu trong Perl 5.14) bật biểu thức chính quy đã cho
cờ cho đến hết phạm vi từ vựng. Xem "chế độ" / flags "để biết thêm chi tiết.

sử dụng re 'debug';
/^(.*)$/s; # thông tin gỡ lỗi đầu ra

sử dụng re 'debugcolor';
/^(.*)$/s; # đầu ra thông tin gỡ lỗi ở màu sống

Các pragmas "gỡ lỗi" và "debugcolor" toàn cầu cho phép một người có được thông tin gỡ lỗi chi tiết về
biên dịch và thực thi regexp. "debugcolor" giống như gỡ lỗi, ngoại trừ gỡ lỗi
thông tin được hiển thị bằng màu sắc trên các thiết bị đầu cuối có thể hiển thị các chuỗi màu của cụm từ.
Đây là đầu ra ví dụ:

% perl -e 'sử dụng lại "gỡ lỗi"; "abc" = ~ / a * b + c /; '
Biên dịch REx 'a * b + c'
kích thước 9 đầu tiên ở 1
1: STAR(4)
2: CHÍNH XÁC (0)
4: PLUS(7)
5: CHÍNH XÁC (0)
7: CHÍNH XÁC (9)
9: END(0)
nổi 'bc' tại 0..2147483647 (đang kiểm tra thả nổi) minlen 2
Đoán thời điểm bắt đầu trận đấu, REx 'a * b + c' so với 'abc' ...
Đã tìm thấy dấu chấm động 'bc' ở độ lệch 1 ...
Đoán: khớp ở độ lệch 0
Khớp REx 'a * b + c' với 'abc'
Đặt phạm vi EVAL, savestack = 3
0 <> | 1: SAO
EXACT có thể khớp 1 lần trong số 32767 ...
Đặt phạm vi EVAL, savestack = 3
1 | 4: CỘNG
EXACT có thể khớp 1 lần trong số 32767 ...
Đặt phạm vi EVAL, savestack = 3
2 | 7: CHÍNH XÁC
3 <> | 9: HẾT
Kết hợp thành công!
Giải phóng REx: 'a * b + c'

Nếu bạn đã hiểu sâu về hướng dẫn này, bạn có thể đoán được điều gì khác biệt
các phần của đầu ra gỡ lỗi cho bạn biết. Phần đầu tiên

Biên dịch REx 'a * b + c'
kích thước 9 đầu tiên ở 1
1: STAR(4)
2: CHÍNH XÁC (0)
4: PLUS(7)
5: CHÍNH XÁC (0)
7: CHÍNH XÁC (9)
9: END(0)

mô tả giai đoạn biên dịch. STAR(4) có nghĩa là có một đối tượng được gắn dấu sao, trong
trường hợp 'a' và nếu nó khớp, chuyển đến dòng 4, tức là, PLUS(7). Các dòng giữa mô tả một số
heuristics và tối ưu hóa được thực hiện trước một trận đấu:

nổi 'bc' tại 0..2147483647 (đang kiểm tra thả nổi) minlen 2
Đoán thời điểm bắt đầu trận đấu, REx 'a * b + c' so với 'abc' ...
Đã tìm thấy dấu chấm động 'bc' ở độ lệch 1 ...
Đoán: khớp ở độ lệch 0

Sau đó, trận đấu được thực hiện và các dòng còn lại mô tả quá trình:

Khớp REx 'a * b + c' với 'abc'
Đặt phạm vi EVAL, savestack = 3
0 <> | 1: SAO
EXACT có thể khớp 1 lần trong số 32767 ...
Đặt phạm vi EVAL, savestack = 3
1 | 4: CỘNG
EXACT có thể khớp 1 lần trong số 32767 ...
Đặt phạm vi EVAL, savestack = 3
2 | 7: CHÍNH XÁC
3 <> | 9: HẾT
Kết hợp thành công!
Giải phóng REx: 'a * b + c'

Mỗi bước có dạng "n ", với " "phần của chuỗi được khớp và" "
phần chưa khớp. "| 1: STAR" nói rằng Perl ở dòng số 1 trong
danh sách biên dịch ở trên. Xem "Gỡ lỗi Cụm từ Thông dụng" trong perldebguts để biết thêm thông tin
chi tiết.

Một phương pháp khác để gỡ lỗi regexps là nhúng các câu lệnh "print" trong
regexp. Điều này cung cấp một tài khoản từng cú đánh của cú đập ngược trong một cách luân phiên:

"that this" = ~ m @ (? {print "Bắt đầu từ vị trí", pos, "\ n";})
t (? {print "t1 \ n";})
h (? {print "h1 \ n";})
i (? {print "i1 \ n";})
s (? {print "s1 \ n";})
|
t (? {print "t2 \ n";})
h (? {print "h2 \ n";})
a (? {print "a2 \ n";})
t (? {print "t2 \ n";})
(? {print "Done at position", pos, "\ n";})
@x;

in

Bắt đầu ở vị trí 0
t1
h1
t2
h2
a2
t2
Thực hiện ở vị trí 4

Sử dụng perlretut trực tuyến bằng các dịch vụ onworks.net


Máy chủ & Máy trạm miễn phí

Tải xuống ứng dụng Windows & Linux

Lệnh Linux

Ad




×
quảng cáo
❤️Mua sắm, đặt phòng hoặc mua tại đây — không mất phí, giúp duy trì các dịch vụ miễn phí.