Quan điểm lịch sử
Bài viết Wikipedia khá chi tiết về nguồn gốc của các biểu thức chính quy (Kleene, 1956). Cú pháp ban đầu là tương đối đơn giản với chỉ *
, +
, ?
, |
và nhóm (...)
. Thật là ngắn gọn ( và có thể đọc được, cả hai không nhất thiết phải đối lập nhau), bởi vì các ngôn ngữ chính thức có xu hướng được thể hiện bằng các ký hiệu toán học ngắn gọn.
Sau đó, cú pháp và khả năng đã phát triển với các biên tập viên và phát triển với Perl , vốn đang cố gắng trở nên ngắn gọn bởi thiết kế ( "các công trình chung nên ngắn" ). Điều này làm phức tạp cú pháp rất nhiều, nhưng lưu ý rằng mọi người giờ đã quen với các biểu thức thông thường và rất giỏi viết (nếu không đọc) chúng. Thực tế là đôi khi chúng chỉ viết cho thấy rằng khi chúng quá dài, chúng thường không phải là công cụ phù hợp.
Biểu thức thông thường có xu hướng không thể đọc được khi bị lạm dụng.
Ngoài các biểu thức chính quy dựa trên chuỗi
Nói về các cú pháp thay thế, chúng ta hãy xem xét một cú pháp đã tồn tại ( cl-ppcre , trong Common Lisp ). Biểu thức chính quy dài của bạn có thể được phân tích cú pháp ppcre:parse-string
như sau:
(let ((*print-case* :downcase)
(*print-right-margin* 50))
(pprint
(ppcre:parse-string "^(?:([A-Za-z]+):)?(\\/{0,3})(0-9.\\-A-Za-z]+)(?::(\\d+))?(?:\\/([^?#]*))?(?:\\?([^#]*))?(?:#(.*))?$")))
... và kết quả ở dạng sau:
(:sequence :start-anchor
(:greedy-repetition 0 1
(:group
(:sequence
(:register
(:greedy-repetition 1 nil
(:char-class (:range #\A #\Z)
(:range #\a #\z))))
#\:)))
(:register (:greedy-repetition 0 3 #\/))
(:register
(:sequence "0-9" :everything "-A-Za-z"
(:greedy-repetition 1 nil #\])))
(:greedy-repetition 0 1
(:group
(:sequence #\:
(:register
(:greedy-repetition 1 nil :digit-class)))))
(:greedy-repetition 0 1
(:group
(:sequence #\/
(:register
(:greedy-repetition 0 nil
(:inverted-char-class #\? #\#))))))
(:greedy-repetition 0 1
(:group
(:sequence #\?
(:register
(:greedy-repetition 0 nil
(:inverted-char-class #\#))))))
(:greedy-repetition 0 1
(:group
(:sequence #\#
(:register
(:greedy-repetition 0 nil :everything)))))
:end-anchor)
Cú pháp này dài dòng hơn, và nếu bạn nhìn vào các bình luận bên dưới, không nhất thiết phải dễ đọc hơn. Vì vậy, đừng cho rằng vì bạn có cú pháp nhỏ gọn hơn nên mọi thứ sẽ tự động rõ ràng hơn .
Tuy nhiên, nếu bạn bắt đầu gặp rắc rối với các biểu thức thông thường, biến chúng thành định dạng này có thể giúp bạn giải mã và gỡ lỗi mã của mình. Đây là một lợi thế so với các định dạng dựa trên chuỗi, trong đó một lỗi ký tự đơn có thể khó phát hiện.
Ưu điểm chính của cú pháp này là thao tác các biểu thức thông thường bằng cách sử dụng định dạng có cấu trúc thay vì mã hóa dựa trên chuỗi. Điều đó cho phép bạn soạn thảo và xây dựng các biểu thức như bất kỳ cấu trúc dữ liệu nào khác trong chương trình của bạn. Khi tôi sử dụng cú pháp trên, điều này thường là vì tôi muốn xây dựng các biểu thức từ các phần nhỏ hơn (xem thêm câu trả lời CodeGolf của tôi ). Ví dụ của bạn, chúng tôi có thể viết 1 :
`(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Các biểu thức chính quy dựa trên chuỗi cũng có thể được tạo, sử dụng nối chuỗi và hoặc nội suy được gói trong các hàm trợ giúp. Tuy nhiên, có những hạn chế với chuỗi các thao tác mà có xu hướng lộn xộn các đang (suy nghĩ về vấn đề làm tổ, không giống như backticks vs $(...)
trong bash; cũng vậy, thoát khỏi nhân vật có thể cung cấp cho bạn đau đầu).
Cũng lưu ý rằng biểu mẫu trên cho phép (:regex "string")
các biểu mẫu để bạn có thể trộn các ký hiệu ngắn gọn với cây. Tất cả điều đó dẫn IMHO đến khả năng đọc và khả năng kết hợp tốt; nó giải quyết ba vấn đề được thể hiện bằng delnan , một cách gián tiếp (tức là không phải bằng ngôn ngữ của chính các biểu thức chính quy).
Để kết luận
Đối với hầu hết các mục đích, ký hiệu terse trên thực tế có thể đọc được. Có những khó khăn khi xử lý các ký hiệu mở rộng liên quan đến quay lui, v.v., nhưng việc sử dụng chúng hiếm khi được biện minh. Việc sử dụng không chính đáng các biểu thức thông thường có thể dẫn đến các biểu thức không thể đọc được.
Các biểu thức thông thường không cần phải được mã hóa dưới dạng chuỗi. Nếu bạn có thư viện hoặc công cụ có thể giúp bạn xây dựng và soạn các biểu thức thông thường, bạn sẽ tránh được rất nhiều lỗi tiềm ẩn liên quan đến thao tác chuỗi.
Ngoài ra, ngữ pháp chính thức dễ đọc hơn và tốt hơn trong việc đặt tên và trừu tượng hóa các biểu thức con. Thiết bị đầu cuối thường được thể hiện dưới dạng biểu thức chính quy đơn giản.
1. Bạn có thể thích xây dựng các biểu thức của mình trong thời gian đọc, bởi vì các biểu thức thông thường có xu hướng là hằng số trong một ứng dụng. Xem create-scanner
và load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )