Tại sao biểu thức chính quy của tôi hoạt động trong X mà không phải trong Y?


76

Tôi đã viết một biểu thức chính quy hoạt động tốt trong một chương trình nhất định (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, đấm). Nhưng khi tôi sử dụng nó trong một chương trình khác (hoặc trên một biến thể unix khác), nó sẽ dừng khớp. Tại sao?

Câu trả lời:


102

Thật không may, vì lý do lịch sử, các công cụ khác nhau có cú pháp biểu thức chính quy hơi khác nhau và đôi khi một số triển khai có phần mở rộng không được các công cụ khác hỗ trợ. Mặc dù có một điểm chung, nhưng dường như mọi nhà văn công cụ đều đưa ra một số lựa chọn khác nhau.

Hậu quả là nếu bạn có một biểu thức chính quy hoạt động trong một công cụ, bạn có thể cần phải sửa đổi nó để hoạt động trong một công cụ khác. Sự khác biệt chính giữa các công cụ phổ biến là:

  • liệu các toán tử +?|(){}yêu cầu một dấu gạch chéo ngược;
  • những tiện ích mở rộng nào được hỗ trợ ngoài những điều cơ bản .[]*^$và thường+?|()

Trong câu trả lời này, tôi liệt kê các tiêu chuẩn chính . Kiểm tra tài liệu của các công cụ bạn đang sử dụng để biết chi tiết.

Wikipedia so sánh các công cụ biểu thức chính quy có một bảng liệt kê các tính năng được hỗ trợ bởi các triển khai chung.

Biểu thức chính quy cơ bản (BRE)

Các biểu thức chính quy cơ bản được mã hóa theo tiêu chuẩn POSIX . Đây là cú pháp được sử dụng bởi grep, sedvi. Cú pháp này cung cấp các tính năng sau:

  • ^$chỉ khớp ở đầu và cuối của một dòng.
  • . phù hợp với bất kỳ nhân vật nào (hoặc bất kỳ nhân vật nào ngoại trừ một dòng mới).
  • […]khớp với bất kỳ một ký tự nào được liệt kê bên trong ngoặc (bộ ký tự). Nếu ký tự đầu tiên sau dấu ngoặc mở là a ^, thì các ký tự không được liệt kê sẽ được khớp với nhau. Để bao gồm a ], đặt nó ngay sau khi mở [(hoặc sau [^nếu đó là một bộ âm). Nếu ở -giữa hai ký tự, nó biểu thị một phạm vi; để bao gồm một nghĩa đen -, đặt nó ở nơi nó không thể được phân tích thành một phạm vi.
  • Dấu gạch chéo ngược trước bất kỳ ^$.*\[dấu ngoặc kép nào của ký tự tiếp theo.
  • * phù hợp với ký tự trước hoặc biểu hiện phụ 0, 1 hoặc nhiều lần.
  • \(…\)là một nhóm cú pháp, để sử dụng với *toán tử hoặc phản hồi và \DIGITthay thế.
  • Backreferences \1, \2... phù hợp với văn bản chính xác kết hợp bởi các nhóm tương ứng, ví dụ như \(fo*\)\(ba*\)\1trận đấu foobaafoonhưng không phải foobaafo. Không có cách tiêu chuẩn để đề cập đến nhóm thứ 10 và hơn thế nữa (ý nghĩa tiêu chuẩn của \10nhóm đầu tiên được theo sau bởi a 0).

Các tính năng sau đây cũng là tiêu chuẩn, nhưng thiếu trong một số triển khai hạn chế:

  • \{m,n\}phù hợp với ký tự trước hoặc biểu hiện phụ giữa m đến n lần; n hoặc m có thể được bỏ qua, và có nghĩa là chính xác m .\{m\}
  • Bên trong ngoặc, các lớp ký tự có thể được sử dụng, ví dụ [[:alpha:]]khớp với bất kỳ chữ cái nào. Các triển khai hiện đại của biểu thức ngoặc ) cũng bao gồm các phần tử đối chiếu như [.ll.]và các lớp tương đương như thế nào [=a=].

Sau đây là các phần mở rộng phổ biến (đặc biệt là trong các công cụ GNU), nhưng chúng không được tìm thấy trong tất cả các triển khai. Kiểm tra hướng dẫn sử dụng của công cụ bạn đang sử dụng.

  • \|để xen kẽ: foo\|barkhớp foohoặc bar.
  • \?(viết tắt của \{0,1\}) và \+(viết tắt \{1,\}) khớp với ký tự trước hoặc biểu hiện phụ tối đa 1 lần hoặc ít nhất 1 lần tương ứng.
  • \nphù hợp với một dòng mới, \tphù hợp với một tab, vv
  • \wkhớp với bất kỳ thành phần từ nào (viết tắt [_[:alnum:]]nhưng có biến thể khi nói đến nội địa hóa) và \Wphù hợp với bất kỳ ký tự nào không phải là thành phần từ.
  • \<và chỉ \>khớp chuỗi trống ở đầu hoặc cuối của một từ tương ứng; \bphù hợp hoặc, và \Btrận đấu \bkhông.

Lưu ý rằng các công cụ không có \|toán tử sẽ không có toàn bộ sức mạnh của biểu thức thông thường. Backreferences cho phép một số điều bổ sung không thể được thực hiện với các biểu thức thông thường theo nghĩa toán học.

Biểu thức chính quy mở rộng (ERE)

Các biểu thức chính quy mở rộng được mã hóa theo tiêu chuẩn POSIX . Ưu điểm chính của họ so với BRE là tính đều đặn: tất cả các toán tử tiêu chuẩn đều là các ký tự dấu chấm câu, dấu gạch chéo ngược trước ký tự dấu chấm câu luôn trích dẫn nó. Đó là cú pháp được sử dụng bởi awk, grep -Ehoặc egrep, toán tử GNU sed -rbash=~ . Cú pháp này cung cấp các tính năng sau:

  • ^$chỉ khớp ở đầu và cuối của một dòng.
  • . phù hợp với bất kỳ nhân vật nào (hoặc bất kỳ nhân vật nào ngoại trừ một dòng mới).
  • […]khớp với bất kỳ một ký tự nào được liệt kê bên trong ngoặc (bộ ký tự). Bổ sung với một ban đầu ^và phạm vi hoạt động như trong BRE (xem ở trên). Các lớp nhân vật có thể được sử dụng nhưng bị thiếu trong một vài triển khai. Các triển khai hiện đại cũng hỗ trợ các lớp tương đương và các yếu tố đối chiếu. Dấu gạch chéo ngược trong ngoặc trích dẫn ký tự tiếp theo trong một số nhưng không phải tất cả các triển khai; sử dụng \\để có nghĩa là dấu gạch chéo ngược cho tính di động.
  • (…)là một nhóm cú pháp, để sử dụng với *hoặc \DIGITthay thế.
  • |để xen kẽ: foo|barkhớp foohoặc bar.
  • *, +?phù hợp với nhân vật trước hoặc subexpression một số lần: 0 hoặc nhiều hơn cho *, 1 hoặc nhiều hơn cho +, 0 hoặc 1 ?.
  • Dấu gạch chéo ngược trích dẫn ký tự tiếp theo nếu nó không phải là chữ và số.
  • {m,n}phù hợp với ký tự trước hoặc biểu hiện phụ giữa mn lần (thiếu trong một số triển khai); n hoặc m có thể được bỏ qua, và có nghĩa là chính xác m .{m}
  • Một số tiện ích mở rộng phổ biến như trong BRE: backreferences (đáng chú ý là không có trong awk ngoại trừ trong triển khai busybox nơi bạn có thể sử dụng ); ký tự đặc biệt , , vv .; ranh giới từ và , từ cấu thành và ,\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (Biểu thức chính quy tương thích Perl)

PCRE là phần mở rộng của ERE, ban đầu được giới thiệu bởi Perl và được GNU grep -Pnhiều công cụ và ngôn ngữ lập trình hiện đại , thông qua thư viện PCRE . Xem tài liệu Perl để định dạng đẹp với các ví dụ. Không phải tất cả các tính năng của phiên bản Perl mới nhất đều được PCRE hỗ trợ (ví dụ: thực thi mã Perl chỉ được hỗ trợ trong Perl). Xem hướng dẫn PCRE để biết tóm tắt các tính năng được hỗ trợ. Các bổ sung chính cho ERE là:

  • (?:…)là một nhóm không bắt giữ: thích (…), nhưng không được tính cho các phản hồi.
  • (?=FOO)BAR(lookahead) các trận đấu BAR, nhưng chỉ khi có một trận đấu để FOObắt đầu ở cùng một vị trí. Điều này hữu ích nhất để neo một trận đấu mà không bao gồm văn bản sau trong trận đấu: foo(?=bar)khớp foonhưng chỉ khi nó được theo sau bar.
  • (?!FOO)BAR(nhìn tiêu cực) phù hợp BAR, nhưng cũng không phải là một trận đấu cho FOOcùng một vị trí. Ví dụ (?!foo)[a-z]+phù hợp với bất kỳ từ viết thường không bắt đầu bằng foo; [a-z]+(?![0-9)phù hợp với bất kỳ từ viết thường không được theo sau bởi một chữ số (vì vậy foo123, nó phù hợp fonhưng không foo).
  • (?<=FOO)BAR(lookbehind) khớp BAR, nhưng chỉ khi nó được bắt đầu ngay trước một trận đấu FOO. FOOphải có độ dài đã biết (bạn không thể sử dụng các toán tử lặp lại như *). Điều này hữu ích nhất để neo một trận đấu mà không bao gồm văn bản trước trong trận đấu: (?<=^| )fookhớp foonhưng chỉ khi nó đứng trước một khoảng trắng hoặc ở đầu chuỗi.
  • (?<!FOO)BAR(cái nhìn tiêu cực) phù hợp BAR, nhưng chỉ khi nó không được bắt đầu ngay trước một trận đấu cho FOO. FOOphải có độ dài đã biết (bạn không thể sử dụng các toán tử lặp lại như *). Điều này hữu ích nhất để neo một trận đấu mà không bao gồm văn bản trước trong trận đấu: (?<![a-z])fookhớp foonhưng chỉ khi nó không được đặt trước một chữ cái viết thường.

Emacs

Cú pháp của Emacs là trung gian giữa BRE và ERE. Ngoài Emacs, nó là cú pháp mặc định -regextrong GNU find. Emacs cung cấp các toán tử sau:

  • ^, $, ., […], *, +, ?Như trong ERE
  • \(…\), \|, \{…\}, Như trong BRE\DIGIT
  • trình tự dấu gạch chéo ngược nhiều hơn ; \<\>cho ranh giới từ; và nhiều hơn nữa trong các phiên bản gần đây của Emacs, thường không được hỗ trợ trong các công cụ khác với cú pháp giống như Emacs.

Shell globs

Shell globs (ký tự đại diện) thực hiện khớp mẫu với cú pháp hoàn toàn khác với biểu thức thông thường và ít mạnh mẽ hơn. Ngoài shell, các ký tự đại diện này có sẵn với các công cụ khác như find -namebộ lọc rsync. Các mẫu POSIX bao gồm các tính năng sau:

  • ? phù hợp với bất kỳ nhân vật duy nhất.
  • […]là một bộ ký tự như trong các cú pháp biểu thức chính quy thông thường. Một số shell không hỗ trợ các lớp nhân vật. Một số shell yêu cầu !thay vì ^phủ định tập hợp.
  • *khớp với bất kỳ chuỗi ký tự nào (thường ngoại trừ /khi khớp đường dẫn tệp; nếu /bị loại trừ *, **đôi khi bao gồm /, nhưng kiểm tra tài liệu của công cụ).
  • Dấu gạch chéo ngược trích dẫn ký tự tiếp theo.

Ksh cung cấp các tính năng bổ sung cung cấp cho mẫu của nó phù hợp với toàn bộ sức mạnh của biểu thức thông thường. Các tính năng này cũng có sẵn trong bash sau khi chạy shopt -s extglob. Zsh có một cú pháp khác nhau nhưng cũng có thể hỗ trợ cú pháp của ksh sau setopt ksh_glob.


Các REs phong phú khác mà bạn có thể muốn đề cập là vimcác RE và AT & T libast (như trong ksh93).
Stéphane Chazelas

@ StéphaneChazelas Ngoài vim, chương trình nào sử dụng regexps vim? Ngoài ksh, chương trình nào sử dụng libast?
Gilles

tất cả các công cụ thiết lập của AT & T sử dụng AT & T RES ( grep, tw, expr...). Ngoại trừ ksh, bộ công cụ đó hiếm khi được tìm thấy bên ngoài AT & T.
Stéphane Chazelas

Theo cách hiểu của tôi (và Wikipedia), thuật ngữ "Lớp nhân vật" của bạn thực sự đề cập đến "lớp ký tự POSIX" ... tuy nhiên, regex(7)đồng ý với bạn và gọi [these]"biểu thức ngoặc" và (trong "biểu thức ngoặc") [:these:]"các lớp ký tự". Tôi không chắc làm thế nào để giải quyết tốt nhất điều đó.
Adam Katz

Bất cứ điều gì bạn gọi cho họ, họ hỗ trợ phạm vi. Điều chắc chắn đáng chú ý là -chỉ định một phạm vi và nên được thoát, trước tiên (sau tùy chọn ^) hoặc cuối cùng nếu nó được thực hiện theo nghĩa đen. (Tôi đã thấy rất nhiều lỗi xuất phát từ ví dụ như chú thích [A-z]sự thay đổi trong trường hợp, phù hợp với các ký tự của mã 65 đến 122 và vô tình bao gồm từng ký tự : [\]^_`. Tôi cũng đã thấy tính hợp lệ nhưng khó hiểu [!-~]để khớp với tất cả các ký tự có thể in trong ANSI , mà tôi thích xem là [\x21-\x7e], ít nhất là đơn giản trong hành động của nó mặc dù khó hiểu ở một chiều khác.)
Adam Katz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.