Vì không ai khác đưa ra câu trả lời trực tiếp cho câu hỏi đã được hỏi , tôi sẽ làm điều đó.
Câu trả lời là với POSIX grep
, không thể đáp ứng yêu cầu này theo nghĩa đen:
grep "<Regex for 'doesn't contain hede'>" input
Lý do là POSIX grep
chỉ được yêu cầu để hoạt động với Biểu thức chính quy cơ bản , đơn giản là không đủ mạnh để hoàn thành nhiệm vụ đó (chúng không có khả năng phân tích ngôn ngữ thông thường, vì thiếu xen kẽ và dấu ngoặc đơn).
Tuy nhiên, GNU grep
thực hiện các phần mở rộng cho phép nó. Cụ thể, \|
là toán tử xen kẽ trong việc triển khai BREs của GNU \(
và \)
là dấu ngoặc đơn. Nếu công cụ biểu thức chính quy của bạn hỗ trợ xen kẽ, biểu thức ngoặc âm, dấu ngoặc đơn và ngôi sao Kleene và có thể neo vào đầu và cuối chuỗi, đó là tất cả những gì bạn cần cho phương pháp này. Tuy nhiên, lưu ý rằng các bộ phủ định [^ ... ]
rất tiện lợi ngoài các bộ đó, bởi vì nếu không, bạn cần thay thế chúng bằng một biểu thức có dạng (a|b|c| ... )
liệt kê mọi ký tự không có trong bộ, cực kỳ tẻ nhạt và quá dài, thậm chí còn hơn thế toàn bộ bộ ký tự là Unicode.
Với GNU grep
, câu trả lời sẽ giống như:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(được tìm thấy với Grail và một số tối ưu hóa khác được thực hiện bằng tay).
Bạn cũng có thể sử dụng một công cụ triển khai Biểu thức chính quy mở rộng , như egrep
, để thoát khỏi dấu gạch chéo ngược:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Đây là một kịch bản để kiểm tra nó (lưu ý rằng nó tạo ra một tệp testinput.txt
trong thư mục hiện tại):
#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"
# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede
h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)
Trong hệ thống của tôi, nó in:
Files /dev/fd/63 and /dev/fd/62 are identical
như mong đợi.
Đối với những người quan tâm đến chi tiết, kỹ thuật được sử dụng là chuyển đổi biểu thức chính quy khớp với từ thành máy tự động hữu hạn, sau đó đảo ngược máy tự động bằng cách thay đổi mọi trạng thái chấp nhận thành không chấp nhận và ngược lại, sau đó chuyển đổi FA kết quả thành một biểu thức chính quy.
Cuối cùng, như mọi người đã lưu ý, nếu công cụ biểu thức chính quy của bạn hỗ trợ giao diện tiêu cực, điều đó sẽ đơn giản hóa công việc rất nhiều. Ví dụ: với GNU grep:
grep -P '^((?!hede).)*$' input
Cập nhật: Gần đây tôi đã tìm thấy thư viện FormTheory tuyệt vời của Kendall Hopkins , được viết bằng PHP, cung cấp một chức năng tương tự như Grail. Sử dụng nó và một trình giả lập do chính tôi viết, tôi đã có thể viết một trình tạo trực tuyến các biểu thức chính quy âm được cung cấp một cụm từ đầu vào (chỉ các ký tự chữ và số và dấu cách hiện được hỗ trợ): http://www.formauri.es/personal/ pgimeno / misc / không khớp-regex /
Đối với hede
đầu ra:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
tương đương với ở trên.
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
? Ý tưởng rất đơn giản. Tiếp tục khớp cho đến khi bạn thấy bắt đầu chuỗi không mong muốn, sau đó chỉ khớp trong các trường hợp N-1 trong đó chuỗi chưa hoàn thành (trong đó N là độ dài của chuỗi). Các trường hợp N-1 này là "h theo sau là không phải e", "anh ta theo sau là không phải d" và "hed theo sau là không phải e". Nếu bạn quản lý để vượt qua các trường hợp N-1 này, bạn đã thành công không khớp với chuỗi không mong muốn để bạn có thể bắt đầu tìm kiếm[^h]*
lại