Khi nào bạn KHÔNG nên sử dụng Biểu thức thông thường? [đóng cửa]


50

Biểu thức chính quy là công cụ mạnh mẽ trong kho vũ khí của lập trình viên, nhưng - có một số trường hợp khi chúng không phải là lựa chọn tốt nhất, hoặc thậm chí hoàn toàn có hại.

Ví dụ đơn giản # 1 đang phân tích cú pháp HTML bằng regrec - một con đường đã biết đến nhiều lỗi. Có lẽ, điều này cũng thuộc tính để phân tích cú pháp nói chung.

Nhưng, có những khu vực rõ ràng khác không có biểu hiện cho các biểu thức thông thường?


ps: " Câu hỏi bạn đặt ra có vẻ chủ quan và có khả năng bị đóng lại. " - do đó, tôi muốn nhấn mạnh rằng tôi quan tâm đến các ví dụ trong đó việc sử dụng regexps được biết là gây ra vấn đề.


9
Phân tích cú pháp HTML bằng regrec không chỉ là "con đường được biết đến với nhiều lỗi". Nó thực sự là không thể .
Kramii phục hồi Monica

19
Không chỉ là không thể, nó còn dẫn đến sự điên rồ và sự nguyền rủa đời đời
Martin Wickman

3
@ Jörg: Regapi chỉ là viết tắt của cụm từ thông dụng.
Joren

3
@ Jörg: Rất đúng là có một sự khác biệt lớn giữa các biểu thức chính quy trong toán học và việc triển khai chúng trong các thư viện phần mềm. Cũng đúng là hầu hết các thư viện biểu thức chính quy đều có các tiện ích mở rộng vượt xa việc chấp nhận các ngôn ngữ thông thường và việc gọi chúng là các biểu thức chính quy không phải lúc nào cũng phù hợp. Tôi đồng ý với bạn rằng có hai khái niệm khác nhau. Nhưng họ có cùng tên; regapi vẫn chỉ là một từ viết tắt, không phải là một thuật ngữ trong chính nó. Rất nhiều ví dụ trên trang web này sử dụng thuật ngữ đầy đủ cho các thư viện phần mềm.
Joren

2
@ Jörg - đây là những ngữ nghĩa. Mặc dù có thể là một ý tưởng tốt để gọi các mẫu này bằng các tên khác nhau (nếu chỉ để tránh "cụm từ thông dụng dành cho ngôn ngữ thông thường" ngụy biện), "biểu thức chính quy" / "biểu thức chính quy" không phải là một nỗ lực rất tốt và chỉ dẫn đến thêm nhầm lẫn.
Kobi

Câu trả lời:


60

Không sử dụng biểu thức thông thường:

  • Khi có trình phân tích cú pháp.

Điều này không giới hạn đối với HTML . Một XML hợp lệ đơn giản không thể được phân tích cú pháp một cách hợp lý bằng một biểu thức chính quy, ngay cả khi bạn biết lược đồ và bạn biết nó sẽ không bao giờ thay đổi.

Đừng thử, ví dụ, phân tích mã nguồn C # . Thay vào đó, phân tích cú pháp để có được cấu trúc cây có ý nghĩa hoặc mã thông báo.

  • Tổng quát hơn, khi bạn có công cụ tốt hơn để thực hiện công việc của mình.

Điều gì nếu bạn phải tìm kiếm một lá thư, cả nhỏ và vốn? Nếu bạn thích biểu thức thông thường, bạn sẽ sử dụng chúng. Nhưng không phải dễ dàng hơn / nhanh hơn / dễ đọc hơn khi sử dụng hai tìm kiếm, lần lượt từng cái một? Có thể trong hầu hết các ngôn ngữ, bạn sẽ đạt được hiệu suất tốt hơn và làm cho mã của bạn dễ đọc hơn.

Ví dụ, mã mẫu trong câu trả lời của Ingo là một ví dụ hay khi bạn không được sử dụng các biểu thức thông thường. Chỉ cần tìm kiếm foo, sau đó cho bar.

  • Khi phân tích chữ viết của con người.

Một ví dụ tốt là một bộ lọc tục tĩu. Nói chung, không chỉ là một ý tưởng tồi để thực hiện nó, mà bạn có thể bị cám dỗ thực hiện nó bằng cách sử dụng các biểu thức thông thường, và bạn sẽ làm sai. Có rất nhiều cách một con người có thể viết một từ, một số, một câu và sẽ được một người khác hiểu, nhưng không phải là biểu hiện thông thường của bạn. Vì vậy, thay vì bắt những lời tục tĩu thực sự, biểu hiện thường xuyên của bạn sẽ dành thời gian làm tổn thương người dùng khác.

  • Khi xác nhận một số loại dữ liệu.

Ví dụ: không xác thực địa chỉ email thông qua biểu thức chính quy. Trong hầu hết các trường hợp, bạn sẽ làm sai. Trong một trường hợp hiếm hoi, bạn sẽ làm đúng và kết thúc với kinh dị mã hóa dài 6 343 ký tự .

Nếu không có các công cụ phù hợp, bạn sẽ phạm sai lầm. Và bạn sẽ nhận thấy chúng vào giây phút cuối cùng, hoặc có thể không bao giờ. Nếu bạn không quan tâm đến mã sạch, bạn sẽ viết một chuỗi hai mươi dòng không có nhận xét, không có khoảng trắng, không có dòng mới.

  • Khi mã của bạn sẽ được đọc. Và sau đó đọc lại, và lặp đi lặp lại, mỗi lần bởi các nhà phát triển khác nhau.

Nghiêm túc mà nói, nếu tôi lấy mã của bạn và phải xem lại hoặc sửa đổi nó, tôi không muốn mất một tuần để cố gắng hiểu rất nhiều chuỗi ký hiệu dài hai mươi dòng.


9
"Nghiêm túc mà nói, nếu tôi lấy mã của bạn và phải xem lại hoặc sửa đổi nó, tôi không muốn mất một tuần để cố gắng hiểu rất nhiều chuỗi ký hiệu dài hai mươi dòng." +1!
funkybro

1
Đây là một câu trả lời tốt hơn nhiều so với người chị em của nó về stack overflow: stackoverflow.com/questions/7553722/ trên
Kobi

1
Nếu bạn đang sử dụng Perl / PCRE (và có thể cả các hương vị regex hiện đại khác nữa), hãy đọc về các chương trình con, đặt tên cho các nhóm và (?(DEFINE))xác nhận;) Bạn có thể viết các biểu thức rất rõ ràng bằng cách sử dụng các ngữ pháp đó rất giống với những gì bạn sẽ viết bằng yacc hoặc tương tự;)
NikiC

2
Sử dụng các biểu thức thông thường để phân tích các từ trong danh sách đen là một lỗi clbuttic.
Dan Ray

Không có lý do nào trên thế giới để tránh ném regex vào một chuỗi như thế nào "<a href='foo'>stuff</a>". Regexes hiện đại không có rắc rối với điều này.
tchrist

18

Điều quan trọng nhất: khi ngôn ngữ bạn đang phân tích cú pháp không phải là ngôn ngữ thông thường .

HTML không phải là ngôn ngữ thông thường và việc phân tích cú pháp bằng một biểu thức chính quy là không thể (không chỉ khó khăn hoặc đường đến mã lỗi).


4
Sai lầm! Nếu bạn đang sử dụng bất kỳ hương vị regex hiện đại nào (Perl, PCRE, Java, .NET, ...), bạn có thể thực hiện đệ quy và xác nhận và do đó có thể phân tích cú pháp ngữ pháp không ngữ cảnh và ngữ cảnh.
NikiC

9
@NikiC. Không sai. "Hương vị regex hiện đại" không phải là biểu thức chính quy (có thể được sử dụng để phân tích các ngôn ngữ thông thường, do đó có tên). Tôi đồng ý rằng với PRE bạn có thể làm nhiều hơn nhưng tôi sẽ không gọi chúng chỉ là "biểu thức chính quy" (như trong câu hỏi ban đầu).
Matteo

1
Các regex hiện đại vượt xa những gì bà của bạn đã dạy rằng các regex có thể làm điều đó theo lời khuyên của cô ấy là không quan trọng. Và ngay cả các biểu thức nguyên thủy cũng có thể xử lý hầu hết các đoạn HTML nhỏ. Việc cấm chăn này là vô lý và không thực tế. Regexes đã được thực hiện cho loại điều này. Và vâng, tôi biết những gì tôi đang nói về .
tchrist

12

Trên stackoverflow người ta thường thấy mọi người yêu cầu các biểu thức chính xác tìm hiểu xem một chuỗi đã cho không chứa cái này hay cái kia. Đây là, IMHO, đảo ngược mục đích của biểu thức chính quy. Ngay cả khi một giải pháp tồn tại (sử dụng các xác nhận phủ định tiêu cực hoặc các công cụ như vậy), thì tốt hơn hết là sử dụng regex cho những gì nó được tạo ra và xử lý trường hợp tiêu cực bằng logic chương trình.

Thí dụ:

# bad
if (/complicated regex that assures the string does NOT conatin foo|bar/) {
    # do something
}

# appropriate
if (/foo|bar/) {
    # error handling
} else {
    # do something
}

1
+1: Một vài lần, tôi đã tránh tự mã hóa vào một góc với các biểu thức bằng cách dừng lại và tự hỏi mình "Được rồi, tôi đang cố gắng gì để khớp?" thay vì "Tôi đang cố tránh điều gì?"

5

Hai trường hợp:

Khi có một cách dễ dàng hơn

  • Hầu hết các ngôn ngữ cung cấp một hàm đơn giản như INSTR để xác định xem một chuỗi có phải là tập con của chuỗi khác không. Nếu đó là những gì bạn muốn làm, hãy sử dụng chức năng đơn giản hơn. Đừng viết biểu thức chính quy của bạn.

  • Nếu có một thư viện có sẵn để thực hiện thao tác chuỗi phức tạp, hãy sử dụng nó thay vì viết biểu thức chính quy của riêng bạn.

Khi biểu thức chính quy không đủ mạnh

  • Nếu bạn cần một trình phân tích cú pháp, hãy sử dụng một trình phân tích cú pháp.

0

Biểu thức chính quy không thể xác định cấu trúc đệ quy . Đây là giới hạn cơ bản.

Lấy JSON - nó là một định dạng khá đơn giản, nhưng vì một đối tượng có thể chứa các đối tượng khác làm giá trị thành viên (sâu tùy ý), cú pháp được đệ quy và không thể được phân tích cú pháp bởi regex. Mặt khác, CSV có thể được phân tích cú pháp bởi regex'es vì ​​nó không chứa bất kỳ cấu trúc đệ quy nào.

Trong các biểu thức chính quy ngắn không cho phép mẫu tham chiếu đến chính nó. Bạn không thể nói: tại thời điểm này trong cú pháp khớp với toàn bộ mẫu một lần nữa. Nói cách khác, các biểu thức chính quy chỉ khớp với tuyến tính, nó không chứa một ngăn xếp mà sẽ cho phép nó theo dõi mức độ sâu sắc của một mẫu lồng nhau.

Lưu ý rằng nó không liên quan gì đến việc định dạng phức tạp hay phức tạp như thế nào. Biểu thức S thực sự rất đơn giản, nhưng không thể được phân tích cú pháp bằng biểu thức chính quy. Mặt khác, CSS2 là một ngôn ngữ khá phức tạp, nhưng không chứa các cấu trúc đệ quy và do đó có thể được phân tích cú pháp bằng biểu thức chính quy. (Mặc dù điều này không đúng với CSS3 do các biểu thức CSS, có cú pháp đệ quy.)

Vì vậy, không phải vì nó xấu hay phức tạp hay dễ bị phân tích cú pháp HTML khi chỉ sử dụng regex. Nó chỉ đơn giản là không thể .

Nếu bạn cần phân tích một định dạng chứa các cấu trúc đệ quy, ít nhất bạn cần bổ sung việc sử dụng các biểu thức chính quy với một ngăn xếp để theo dõi mức độ của các cấu trúc đệ quy. Đây thường là cách trình phân tích cú pháp hoạt động. Các biểu thức chính quy được sử dụng để nhận ra các phần "tuyến tính", trong khi mã tùy chỉnh bên ngoài regex được sử dụng để theo dõi các cấu trúc lồng nhau.

Thông thường phân tích cú pháp như thế này được chia thành các giai đoạn riêng biệt. Mã thông báo là giai đoạn đầu tiên trong đó các biểu thức chính quy được sử dụng để phân tách đầu vào thành một chuỗi "mã thông báo" như các từ, dấu chấm câu, ngoặc, v.v. Phân tích cú pháp là giai đoạn tiếp theo trong đó các mã thông báo này được phân tích thành cấu trúc phân cấp, cây cú pháp.

Vì vậy, khi bạn nghe rằng HTML hoặc C # không thể được phân tích cú pháp bởi các biểu thức thông thường, hãy lưu ý rằng các biểu thức chính quy vẫn là một phần quan trọng của trình phân tích cú pháp. Bạn không thể phân tích ngôn ngữ như vậy bằng cách chỉ sử dụng các biểu thức thông thường và không có mã trợ giúp.

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.