अद्वितीय अक्षरों के साथ सभी 10 अक्षर शब्दों के लिए रेगेक्स


23

मैं एक regex लिखने की कोशिश कर रहा हूं जो 10 वर्णों वाले सभी शब्दों को प्रदर्शित करेगा, और कोई भी अक्षर दोहरा नहीं रहा है।

अब तक, मुझे मिल गया है

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

जो प्रश्न का पहला भाग है। मैं "विशिष्टता" के लिए जाँच कैसे करूँगा? मेरे पास वास्तव में कोई सुराग नहीं है, इसके अलावा मुझे वापस संदर्भों का उपयोग करने की आवश्यकता है।


1
यह एक रेगेक्स के साथ किया जाना चाहिए?
हौके लैजिंग

मैं रेगेक्स का अभ्यास कर रहा हूं, इसलिए अधिमानतः हां :)
डायलन मिउस

3
मुझे विश्वास नहीं है कि आप एक कंप्यूटर-विज्ञान शैली की नियमित अभिव्यक्ति के साथ ऐसा कर सकते हैं: आप जो चाहते हैं, उसके लिए "स्मृति" की आवश्यकता है जो कि पूर्ववर्ती मिलान किए गए वर्ण हैं, और नियमित रूप से अभिव्यक्ति के पास ऐसा नहीं है। उस ने कहा, आप इसे वापस संदर्भ और गैर-नियमित-अभिव्यक्ति चीजों के साथ करने में सक्षम हो सकते हैं जो पीसीआरई-शैली मिलान कर सकते हैं।
ब्रूस एडिगर

3
@BruceEdiger जब तक भाषा में वर्णों की एक सीमित संख्या (26) और स्ट्रिंग (10) में अक्षर हैं, तब तक ऐसा करना संभव है। यह सिर्फ बहुत सारे राज्य हैं, लेकिन ऐसा कुछ भी नहीं है जो इसे एक नियमित भाषा नहीं बनाएगा।

1
क्या आपका मतलब है "सभी अंग्रेजी शब्द ..."? क्या आप हाइफ़न और एपोस्ट्रोफ़ के साथ वर्तनी वाले लोगों को शामिल करने का मतलब है या नहीं (इन-लॉ, नहीं)? क्या आप कैफे, भोले, बहाना जैसे शब्दों को शामिल करना चाहते हैं?
हिप्पिएट्रैल

जवाबों:


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

ऐसे शब्दों को शामिल करता है जिनमें दो समान अक्षर होते हैं।

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

वर्णों को दोहराने वाले लोगों को बाहर करता है।

POSIXly:

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

trsगैर-शब्द-वर्णों ( cअल्फा-न्यूमेरिक और अंडरस्कोर को लागू करना) के किसी भी समीकरण को एक नए वर्ण में परिवर्तित करके अपनी स्वयं की लाइन पर शब्द डालते हैं ।

या एक के साथ grep:

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

(कम से कम 10 और 10 से अधिक वर्णों की रेखाओं को छोड़कर और कम से कम दो बार दिखने वाले वर्ण के साथ)।

grepकेवल एक के साथ (GNU grep PCRE समर्थन के साथ या pcregrep):

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

यही है, एक शब्द सीमा ( \b) 10 शब्द वर्णों के अनुक्रम के बाद (बशर्ते कि प्रत्येक शब्द वर्णों के अनुक्रम के बाद और नकारात्मक लुक-अप पीसीआर ऑपरेटर का उपयोग करके स्वयं नहीं है (?!...))।

हम भाग्यशाली हैं कि यह यहाँ काम करता है, क्योंकि कई रेगीक्सप इंजन दोहराव वाले भागों के अंदर काम नहीं करते हैं।

ध्यान दें कि (कम से कम GNU grep के मेरे संस्करण के साथ)

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

काम नहीं करता है, लेकिन

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

करता है (के रूप में echo aa | grep -Pw '(.)\2') जो एक बग की तरह लगता है।

आप चाहेंगे:

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

यदि आप चाहते हैं \wया \bकिसी अक्षर को शब्द घटक के रूप में माना जाए और न केवल ASCII को गैर-ASCII स्थानों में।

एक अन्य विकल्प:

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

यह एक शब्द सीमा है (एक शब्द वर्णों के अनुक्रम के बाद जिसका दोहराव नहीं होता है) जिसके बाद 10 शब्द अक्षर होते हैं।

संभवतः किसी के दिमाग के पीछे की चीजें:

  • तुलनात्मक मामला संवेदनशील है, इसलिए Babylonishउदाहरण के लिए मिलान किया जाएगा, क्योंकि सभी वर्ण अलग-अलग होते हैं, भले ही दो Bएस, एक कम और एक ऊपरी मामला ( -iउस को बदलने के लिए उपयोग करें)।
  • के लिए -w, \wऔर \b, एक शब्द एक पत्र है (केवल ASCII के लिए केवल GNU के grep लिए , [:alpha:]यदि आपके लोकेल में वर्ण वर्ग का उपयोग करके -Pऔर (*UCP)), दशमलव अंक या अंडरस्कोर
  • इसका मतलब है कि c'est(एक शब्द की फ्रांसीसी परिभाषा के अनुसार दो शब्द) या it's(एक शब्द की कुछ अंग्रेजी परिभाषाओं के अनुसार एक शब्द) या rendez-vous(एक शब्द की फ्रांसीसी परिभाषा के अनुसार एक शब्द) एक शब्द नहीं माना जाता है।
  • यहां तक ​​कि (*UCP), यूनिकोड संयोजन वर्णों को शब्द घटकों के रूप में नहीं माना जाता है, इसलिए téléphone( $'t\u00e9le\u0301phone') को 10 वर्णों में से एक माना जाता है, जिनमें से एक गैर-अल्फ़ा है। défavorisé( $'d\u00e9favorise\u0301') मिलान किया जाएगा भले ही यह दो éहो गया है क्योंकि यह 10 अलग-अलग अल्फा वर्ण हैं जिनके बाद एक तीव्र उच्चारण (गैर-अल्फा का संयोजन होता है, इसलिए इसमें eऔर इसके उच्चारण के बीच एक शब्द सीमा है )।

1
बहुत बढ़िया। हालांकि \wमेल नहीं खाता -
ग्रीम

@ स्टेपहेन क्या आप पिछले दो भावों की संक्षिप्त व्याख्या कर सकते हैं।
mkc

कभी-कभी ऐसा लगता है कि लुकरॉइड्स उन सभी चीजों का समाधान है जो आरई के साथ असंभव हुआ करते थे।
बरमेर

1
@ बरमार वे अभी भी नियमित अभिव्यक्तियों के साथ असंभव हैं। एक "रेगुलर एक्सप्रेशन" एक गणितीय निर्माण है जो स्पष्ट रूप से केवल कुछ निर्माणों को अनुमति देता है, अर्थात् शाब्दिक वर्ण, वर्ण वर्ग, और '|', '(...)', '?', '+' और '*' संचालक। कोई भी तथाकथित "नियमित अभिव्यक्ति" जो एक ऑपरेटर का उपयोग करता है जो उपरोक्त में से एक नहीं है, वास्तव में एक नियमित अभिव्यक्ति नहीं है।
जूल्स

1
@Jules यह unix.stackexchange.com है, math.stackexchange.com नहीं है। गणितीय आरईआर इस संदर्भ में अप्रासंगिक हैं, हम बात कर रहे हैं कि आप किस प्रकार के आरईएस के बारे में उपयोग कर रहे हैं grep, PCRE, आदि
बरमार

12

ठीक है ... यहाँ पाँच चरित्र स्ट्रिंग के लिए क्लूनी तरीका है:

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

क्योंकि आप एक चरित्र वर्ग (जैसे में एक वापस संदर्भ नहीं डाल सकते [^\1|\2]), तो आप का उपयोग करना चाहिए नकारात्मक लुक-आगे - (?!foo)। यह एक पीसीआरई सुविधा है जिससे आपको -Pस्विच की आवश्यकता होती है ।

10 वर्ण स्ट्रिंग के लिए पैटर्न बहुत लंबा होगा, ज़ाहिर है, लेकिन लुकहेड में एक चर लंबाई के कुछ मैच ('*।') का उपयोग करके एक छोटा तरीका है:

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

स्टीफन चेज़लस के ज्ञानवर्धक उत्तर को पढ़ने के बाद, मैंने महसूस किया कि इस प्रयोग के लिए grep के -vस्विच के माध्यम से एक समान सरल पैटर्न है :

    (.).*\1

चूंकि चेक एक समय में एक वर्ण को आगे बढ़ाता है, यह देखेगा कि क्या किसी दिए गए वर्ण का अनुसरण शून्य या अधिक वर्णों ( .*) के बाद किया गया है और फिर वापस संदर्भ के लिए एक मैच। -vinverts, केवल उन चीजों को प्रिंट करना जो इस पैटर्न से मेल नहीं खाते हैं। यह बैक सन्दर्भ को अधिक उपयोगी बनाता है क्योंकि उन्हें चरित्र वर्ग के साथ नकारा नहीं जा सकता है, और महत्वपूर्ण रूप से:

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

अद्वितीय वर्णों के साथ किसी भी लम्बाई के तार की पहचान करने के लिए काम करेंगे:

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

क्योंकि यह अद्वितीय अक्षर के साथ जो कुछ भी प्रत्यय (जैसे की पूर्ति करेंगे, नहीं होगा, abcabcकी वजह से मेल खाता है abcअंत में, और aaaaकी वजह से a- इसलिए अंत में किसी भी स्ट्रिंग)। यह लुकरॉइड्स के शून्य-चौड़ाई होने के कारण एक जटिलता है (वे कुछ भी उपभोग नहीं करते हैं)।


बहुत बढ़िया! यह केवल क्यू में हालांकि एक के साथ संयोजन में काम करेगा।
ग्रीम

1
मेरा मानना ​​है कि आप पहले एक को सरल कर सकते हैं यदि आपका रेगेक्स इंजन वैरिएबल-लेंथ निगेटिव (.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!\4).
लुकहेड की

@ChristopherCreutzig: बिलकुल, अच्छी कॉल। मैंने इसमें जोड़ दिया है।
गोल्डीलॉक्स

6

यदि आपको regex में पूरी बात करने की आवश्यकता नहीं है, तो मैं इसे दो चरणों में करूंगा: पहले सभी 10-अक्षर के शब्दों का मिलान करें, फिर उन्हें विशिष्टता के लिए फ़िल्टर करें। सबसे छोटा तरीका मुझे पता है कि यह कैसे करना है पर्ल में:

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

अतिरिक्त \Wएंकरों पर ध्यान दें कि यह सुनिश्चित करने के लिए कि केवल 10 अक्षर लंबे शब्द मेल खाते हैं।


धन्यवाद, लेकिन मैं इसे एक regex oneliner के रूप में पसंद करूँगा :)
डायलन Meeus

4

दूसरों ने सुझाव दिया है कि यह कुछ नियमित अभिव्यक्ति प्रणालियों के लिए विभिन्न एक्सटेंशन के बिना संभव नहीं है जो वास्तव में नियमित नहीं हैं। हालाँकि, चूंकि आप जिस भाषा से मैच करना चाहते हैं वह परिमित है, यह स्पष्ट रूप से नियमित है। 4-अक्षर वर्णमाला के 3 अक्षरों के लिए, यह आसान होगा:

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

जाहिर है यह अधिक पत्र और बड़े अक्षर के साथ जल्दी में हाथ से निकल जाता है। :-)


मुझे इसे आगे बढ़ाना पड़ा क्योंकि यह वास्तव में एक जवाब है जो काम करेगा। हालांकि यह वास्तव में सबसे कम कुशल तरीका हो सकता है किसी ने भी रेगेक्स को कभी लिखा है: पी
डायलन मीस

4

GNU का विकल्प --perl-regexp(संक्षिप्त -P) grepअधिक शक्तिशाली नियमित अभिव्यक्तियों का उपयोग करता है जिसमें आगे के पैटर्न दिखते हैं। प्रत्येक अक्षर के लिए निम्न पैटर्न दिखता है कि यह अक्षर शेष शब्द में नहीं दिखता है:

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

हालांकि रन-टाइम व्यवहार काफी बुरा है, क्योंकि \w*इसमें लगभग अनंत लंबाई हो सकती है। इसे सीमित किया जा सकता है \w{,8}, लेकिन यह भी 10 अक्षरों की शब्द सीमा से परे की जाँच करता है। इसलिए निम्न पैटर्न पहले सही शब्द की लंबाई की जाँच करता है:

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

परीक्षण फ़ाइल के रूप में मैंने एक बड़ी file 500 एमबी फ़ाइल का उपयोग किया है:

  • पहला पैटर्न: ≈ 43 एस
  • लैटर पैटर्न: ≈ 15 एस

अद्यतन करें:

मैं एक गैर-लालची ऑपरेटर ( \w*?) या अधिकार प्राप्त ऑपरेटर ( (...){10}+) के लिए रन-टाइम व्यवहार में एक महत्वपूर्ण परिवर्तन नहीं ढूंढ सका । एक छोटे से तेजी से विकल्प के प्रतिस्थापन लगता है -w:

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

संस्करण 2.13 से 2.18 तक grep का एक अद्यतन बहुत अधिक प्रभावी था। परीक्षण फ़ाइल केवल। 6 एस ले गई।


प्रदर्शन डेटा की प्रकृति पर बहुत कुछ निर्भर करेगा। खदान पर परीक्षण करते समय, मैंने पाया कि गैर-लालची ऑपरेटरों ( \w{,8}?) ने कुछ प्रकार के इनपुट के लिए मदद की (हालांकि बहुत महत्वपूर्ण नहीं)। \g{-1}GNU grep बग के आसपास काम करने के लिए अच्छा उपयोग ।
स्टीफन चेज़लस

@StephaneChazelas: प्रतिक्रिया के लिए धन्यवाद। मैंने गैर-लालची और अधिकारी संचालकों की भी कोशिश की थी और रन-टाइम व्यवहार (संस्करण 2.13) में महत्वपूर्ण बदलाव नहीं पाया है। संस्करण 2.18 बहुत तेज है और मैं कम से कम एक छोटा सा सुधार देख सकता था। GNU grep बग दोनों संस्करणों में मौजूद है। वैसे भी मैं सापेक्ष संदर्भ पसंद करता हूं \g{-1}, क्योंकि यह स्थान पर पैटर्न को अधिक स्वतंत्र बनाता है। इस रूप में इसे एक बड़े पैटर्न के हिस्से के रूप में इस्तेमाल किया जा सकता है।
20

0

एक पर्ल समाधान:

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

लेकिन यह साथ काम नहीं करता है

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

या

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

perl v5.14.2 और v5.18.2 के साथ परीक्षण किया गया


1 और 3 कुछ भी नहीं करता है, 2 लगातार 10 या अधिक वर्णों की किसी भी पंक्ति को आउटपुट करता है, जिसमें 2 से अधिक लगातार रिक्त स्थान नहीं हैं। pastebin.com/eEDcy02D
मैनटवर्क

यह शायद पर्ल संस्करण है। v5.14.2 और v5.18.2 के साथ परीक्षण किया गया

मैंने उन्हें लिनक्स पर v5.14.1 और Cygwin पर v5.14.2 के साथ आजमाया। दोनों ने पास्टबिन के नमूने की तरह व्यवहार किया जो मैंने पहले जोड़ा था।
मैनटवर्क

पहली पंक्ति पर्ल के विख्यात संस्करणों के साथ मेरे लिए काम करती है। दो बाद में काम करना चाहिए, क्योंकि वे एक ही हैं, लेकिन नहीं किया। perlre अक्सर ध्यान दें कि कुछ लालची अभिव्यक्ति अत्यधिक प्रयोगात्मक हैं।

अपने नवीनतम अपडेट के साथ सेवानिवृत्त। केवल 2 एक ही सही ढंग से आउटपुट। (हालाँकि यह शब्द एक पंक्ति में अकेला होना चाहिए, जबकि सवाल शब्दों के मिलान के बारे में है, न कि पूरी पंक्तियों के बारे में।)
मैनटवर्क
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.