Regex cho tất cả 10 chữ cái, với các chữ cái duy nhất


23

Tôi đang cố gắng viết một regex sẽ hiển thị tất cả các từ dài 10 ký tự và không có chữ cái nào được lặp lại.

Cho đến nay, tôi đã có

grep --colour -Eow '(\w{10})'

Đó là phần đầu tiên của câu hỏi. Làm thế nào tôi có thể kiểm tra "tính duy nhất"? Tôi thực sự không có manh mối, ngoài ra tôi cần sử dụng các tài liệu tham khảo trở lại.


1
Điều này phải được thực hiện với một regex?
Hauke ​​Laging

Tôi đang luyện tập regex, vì vậy tốt nhất là có :)
Dylan Meeus

3
Tôi không tin rằng bạn có thể làm điều này với biểu thức chính quy theo kiểu khoa học máy tính: những gì bạn muốn đòi hỏi "bộ nhớ" về các ký tự trùng khớp trước đó là gì và các biểu thức thông thường không có điều đó. Điều đó nói rằng, bạn có thể có thể làm điều đó với các tham chiếu trở lại và những điều không biểu hiện chính quy mà kết hợp kiểu PCRE có thể làm.
Bruce Ediger

3
@BruceEdiger miễn là có một số lượng ký tự hữu hạn trong ngôn ngữ (26) và các chữ cái trong chuỗi (10), điều đó hoàn toàn có thể làm được. Nó chỉ là rất nhiều trạng thái, nhưng không có gì có thể làm cho nó không phải là một ngôn ngữ thông thường.

1
Bạn có nghĩa là "Tất cả các từ tiếng Anh ..."? Bạn có nghĩa là bao gồm những từ được đánh vần bằng dấu gạch ngang và dấu nháy đơn hay không (trong luật, không)? Bạn có nghĩa là bao gồm các từ như quán cà phê, ngây thơ, mặt tiền?
hà mã

Câu trả lời:


41
grep -Eow '\w{10}' | grep -v '\(.\).*\1'

loại trừ các từ có hai ký tự giống hệt nhau.

grep -Eow '\w{10}' | grep -v '\(.\)\1'

loại trừ những cái có ký tự lặp lại.

POSIXly:

tr -cs '[:alnum:]_' '[\n*]' |
   grep -xE '.{10}' |
   grep -v '\(.\).*\1'

trđặt các từ trên dòng riêng của chúng bằng cách chuyển đổi bất kỳ phương trình nào scủa các ký tự không phải từ (bao cgồm số alpha và số gạch dưới) thành ký tự dòng mới.

Hoặc với một grep:

tr -cs '[:alnum:]_' '[\n*]' |
   grep -ve '^.\{0,9\}$' -e '.\{11\}' -e '\(.\).*\1'

(loại trừ các dòng dưới 10 và hơn 10 ký tự và các dòng có ký tự xuất hiện ít nhất hai lần).

Chỉ với một grep(GNU grep có hỗ trợ PCRE hoặc pcregrep):

grep -Po '\b(?:(\w)(?!\w*\1)){10}\b'

Đó là, một ranh giới từ ( \b) theo sau là một chuỗi gồm 10 ký tự từ (với điều kiện là mỗi ký tự không được theo sau bởi một chuỗi các ký tự từ và chính chúng, sử dụng toán tử PCRE nhìn phía trước tiêu cực (?!...)).

Chúng tôi rất may mắn khi nó hoạt động ở đây, vì không có nhiều công cụ regrec hoạt động với các phản hồi bên trong các bộ phận lặp lại.

Lưu ý rằng (với phiên bản GNU grep của tôi ít nhất)

grep -Pow '(?:(\w)(?!\w*\1)){10}'

Không hoạt động, nhưng

grep -Pow '(?:(\w)(?!\w*\2)){10}'

không (như echo aa | grep -Pw '(.)\2') mà âm thanh như một lỗi.

Bạn có thể muốn:

grep -Po '(*UCP)\b(?:(\w)(?!\w*\1)){10}\b'

nếu bạn muốn \whoặc \bcoi bất kỳ chữ cái nào là một thành phần từ và không chỉ các chữ cái ASCII ở các địa phương không phải ASCII.

Một cách khác:

grep -Po '\b(?!\w*(\w)\w*\1)\w{10}\b'

Đó là một ranh giới từ (một không được theo sau bởi một chuỗi các ký tự từ mà một trong số đó lặp lại) theo sau là 10 ký tự từ.

Những điều có thể có ở phía sau tâm trí của một người:

  • So sánh là trường hợp nhạy cảm, do đó, Babylonishví dụ sẽ được khớp, vì tất cả các ký tự đều khác nhau mặc dù có hai Bs, một chữ thường và một chữ hoa (sử dụng -iđể thay đổi điều đó).
  • cho -w, \w\b, một từ là một chữ cái (cái ASCII chỉ cho GNU grep cho bây giờ , các [:alpha:]lớp nhân vật trong miền địa phương của bạn nếu sử dụng -P(*UCP)), chữ số thập phân hoặc dấu gạch dưới .
  • điều đó có nghĩa là c'est(hai từ theo định nghĩa tiếng Pháp của một từ) hoặc it's(một từ theo một số định nghĩa tiếng Anh của một từ) hoặc rendez-vous(một từ theo định nghĩa tiếng Pháp của một từ) không được coi là một từ.
  • Ngay cả với (*UCP), các ký tự kết hợp Unicode không được coi là thành phần từ, vì vậy téléphone( $'t\u00e9le\u0301phone') được coi là 10 ký tự, một trong số đó không phải là alpha. défavorisé( $'d\u00e9favorise\u0301') sẽ được kết hợp ngay cả khi nó có hai évì đó là 10 ký tự alpha khác nhau được theo sau bởi một dấu cấp tính kết hợp (không phải alpha, do đó, có một ranh giới từ giữa dấu evà dấu của nó).

1
Tuyệt vời. \wkhông phù hợp -mặc dù.
Graeme

@Stephane Bạn có thể gửi một lời giải thích ngắn gọn về hai biểu thức cuối cùng.
mkc

Đôi khi có vẻ như ngoại hình là giải pháp cho tất cả những điều không thể có với RE.
Barmar

1
@Barmar họ vẫn không thể với Biểu thức thông thường. "Biểu thức chính quy" là một cấu trúc toán học chỉ cho phép rõ ràng các cấu trúc nhất định, cụ thể là các ký tự chữ, các lớp ký tự và các toán tử '|', '(...)', '?', '+' Và '*'. Bất kỳ cái gọi là "biểu thức chính quy" nào sử dụng toán tử không phải là một trong các biểu thức trên không thực sự là Biểu thức chính quy.
Jules

1
@Jules Đây là unix.stackexchange.com, không phải math.stackexchange.com. Các RE toán học không liên quan trong bối cảnh này, chúng ta đang nói về các loại RE bạn sử dụng với grep, PCRE, v.v.
Barmar 23/214

12

Được rồi ... đây là cách khó hiểu cho chuỗi năm ký tự:

grep -P '^(.)(?!\1)(.)(?!\1|\2)(.)(?!\1|\2|\3)(.)(?!\1|\2|\3|\4).$'

Vì bạn không thể đặt tham chiếu ngược trong lớp ký tự (ví dụ [^\1|\2]), bạn phải sử dụng giao diện phủ định - (?!foo). Đây là một tính năng PCRE nên bạn cần -Pchuyển đổi.

Tất nhiên, mẫu cho chuỗi 10 ký tự sẽ dài hơn rất nhiều, nhưng có một phương thức ngắn hơn bằng cách sử dụng độ dài thay đổi bất cứ thứ gì khớp ('. *') Trong giao diện:

grep -P '^(.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!.*\4)(.)(?!.*\5).$'

Sau khi đọc câu trả lời khai sáng của Stephane Chazelas, tôi nhận ra có một mô hình đơn giản tương tự cho việc sử dụng này thông qua -vcông tắc của grep :

    (.).*\1

Vì kiểm tra tiến hành một ký tự tại một thời điểm, điều này sẽ xem liệu có bất kỳ ký tự đã cho nào được theo sau bởi 0 hoặc nhiều ký tự ( .*) và sau đó khớp với tham chiếu trở lại. -vđảo ngược, chỉ in những thứ không phù hợp với mẫu này. Điều này làm cho các tham chiếu phía sau trở nên hữu ích hơn vì chúng không thể bị phủ định với một lớp ký tự và đáng kể:

grep -v '\(.\).*\1'

sẽ hoạt động để xác định một chuỗi có độ dài bất kỳ với các ký tự duy nhất trong khi:

grep -P '(.)(?!.*\1)'

sẽ không, vì nó sẽ khớp bất kỳ hậu tố nào với các ký tự duy nhất (ví dụ: abcabckhớp vì abcở cuối và aaaaaở cuối - do đó là bất kỳ chuỗi nào ). Đây là một biến chứng gây ra bởi ngoại hình có chiều rộng bằng không (chúng không tiêu thụ bất cứ thứ gì).


Làm tốt! Điều này sẽ chỉ hoạt động kết hợp với một trong Q mặc dù.
Graeme

1
Tôi tin rằng bạn có thể đơn giản hóa cái đầu tiên nếu công cụ regex của bạn cho phép cái nhìn tiêu cực có chiều dài thay đổi:(.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!\4).
Christopher Creutzig

@ChristopherCreutzig: Cuộc gọi tuyệt vời. Tôi đã thêm nó vào.
goldilocks

6

Nếu bạn không cần phải làm toàn bộ trong regex, tôi sẽ thực hiện theo hai bước: đầu tiên khớp tất cả các từ có 10 chữ cái, sau đó lọc chúng cho độc đáo. Cách ngắn nhất tôi biết cách làm điều này là trong Perl:

perl -nle 'MATCH:while(/\W(\w{10})\W/g){
             undef %seen;
             for(split//,$1){next MATCH if ++$seen{$_} > 1}
             print
           }' your_file

Lưu ý các \Wneo bổ sung để đảm bảo rằng chỉ các từ dài chính xác 10 ký tự được khớp.


Cảm ơn bạn, nhưng tôi thích nó như một oneliner regex :)
Dylan Meeus

4

Những người khác đã đề xuất điều này là không thể nếu không có các phần mở rộng khác nhau cho một số hệ thống biểu thức chính quy không thực sự thường xuyên. Tuy nhiên, vì ngôn ngữ bạn muốn kết hợp là hữu hạn, nên rõ ràng là thông thường. Đối với 3 chữ cái trong bảng chữ cái 4 chữ cái, thật dễ dàng:

(abc|abd|acb|acd|bac|bad|bcd|bdc|cab|cad|cbd|cdb|dab|dac|dbc|dcb)

Rõ ràng điều này vượt quá tầm tay với nhiều chữ cái và bảng chữ cái lớn hơn. :-)


Tôi đã phải nâng cao điều này bởi vì nó thực sự là một câu trả lời sẽ hoạt động. Mặc dù nó thực sự có thể là cách kém hiệu quả nhất mà bất kỳ ai cũng đã viết regex từ trước đến nay: P
Dylan Meeus 23/214

4

Tùy chọn --perl-regexp(viết tắt -P) của GNU grepsử dụng các biểu thức chính quy mạnh hơn bao gồm các mẫu nhìn về phía trước. Mẫu sau tìm kiếm mỗi chữ cái mà chữ cái này không xuất hiện trong phần còn lại của từ:

grep -Pow '((\w)(?!\w*\g{-1})){10}'

Tuy nhiên, hành vi thời gian chạy là khá xấu, bởi vì \w*có thể có chiều dài gần như vô hạn. Nó có thể bị giới hạn \w{,8}, nhưng điều đó cũng kiểm tra vượt quá giới hạn từ 10 chữ cái. Do đó, mẫu sau đây trước tiên kiểm tra độ dài từ chính xác:

grep -Pow '(?=\w{10}\b)((\w)(?!\w*\g{-1})){10}'

Là tệp thử nghiệm, tôi đã sử dụng tệp ≈ 500 MB lớn:

  • Mẫu thứ nhất: ≈ 43 s
  • Mẫu sau: 15 s

Cập nhật:

Tôi không thể tìm thấy một sự thay đổi đáng kể trong hành vi thời gian chạy cho một toán tử không tham lam ( \w*?) hoặc toán tử sở hữu ( (...){10}+). Nhanh hơn một chút dường như thay thế tùy chọn -w:

grep -Po '\b(?=\w{10}\b)((\w)(?!\w*\g{-1})){10}\b'

Một bản cập nhật của grep từ phiên bản 2.13 đến 2.18 hiệu quả hơn nhiều. Các tập tin thử nghiệm chỉ mất 6 giây.


Hiệu suất sẽ phụ thuộc rất nhiều vào bản chất của dữ liệu. Khi thực hiện các thử nghiệm trên máy của tôi, tôi thấy rằng việc sử dụng các toán tử không tham lam ( \w{,8}?) đã giúp cho một số loại đầu vào (mặc dù không đáng kể lắm). Sử dụng tốt \g{-1}để làm việc xung quanh lỗi GNU grep.
Stéphane Chazelas

@StephaneChazelas: Cảm ơn bạn đã phản hồi. Tôi cũng đã thử các toán tử không tham lam và sở hữu và không tìm thấy một sự thay đổi đáng kể trong hành vi thời gian chạy (phiên bản 2.13). Phiên bản 2.18 nhanh hơn nhiều và tôi có thể thấy ít nhất một chút cải tiến. Lỗi GNU grep có trong cả hai phiên bản. Dù sao, tôi thích tham chiếu tương đối \g{-1}, bởi vì nó làm cho mô hình độc lập hơn về vị trí. Trong hình thức này, nó có thể được sử dụng như một phần của một mô hình lớn hơn.
Heiko Oberdiek

0

Một giải pháp Perl:

perl -lne 'print if (!/(.)(?=$1)/g && /^\w{10}$/)' file

nhưng nó không hoạt động với

perl -lne 'print if (!/(.)(?=\1)/g && /^\w{10}$/)' file

hoặc là

perl -lne 'print if ( /(.)(?!$1)/g && /^\w{10}$/)' file

đã thử nghiệm với perl v5.14.2 và v5.18.2


Cái thứ 1 và thứ 3 không làm gì cả, thứ 2 xuất ra bất kỳ dòng nào có từ 10 ký tự trở lên, không quá 2 khoảng trắng liên tiếp. pastebin.com/eEDcy02D
manatwork

nó có lẽ là phiên bản perl đã thử nghiệm với v5.14.2 và v5.18.2

Tôi đã thử chúng với v5.14.1 trên Linux và v5.14.2 trên Cygwin. Cả hai hành xử như trong mẫu pastebin tôi liên kết trước đó.
manatwork

dòng đầu tiên hoạt động với tôi với các phiên bản được chú ý của perl. cả hai nên hoạt động, bởi vì chúng giống nhau, nhưng không. perlre lưu ý rằng một số biểu hiện tham lam có tính thử nghiệm cao.

Kiểm tra lại với bản cập nhật mới nhất của bạn. Chỉ có cái thứ 2 xuất ra chính xác. (Tuy nhiên, từ phải ở một mình trong một dòng, trong khi câu hỏi là về các từ khớp, không phải toàn bộ dòng.)
manatwork
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.