고유 한 문자가 포함 된 10 개의 모든 단어에 대한 정규식


23

10 자 길이의 모든 단어를 표시하는 정규 표현식을 작성하려고하는데 문자가 반복되지 않습니다.

지금까지 나는

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

질문의 첫 부분입니다. "고유성"을 확인하는 방법은 무엇입니까? 역 참조를 사용해야한다는 점을 제외하고는 실마리가 없습니다.


1
이것은 정규식으로 수행해야합니까?
Hauke ​​Laging

나는 :) 너무 바람직 네, 정규식을 연습하고
딜런 Meeus

3
나는 당신이 컴퓨터 과학 스타일의 정규 표현으로 이것을 할 수 있다고 믿지 않습니다 : 당신이 원하는 것은 이전에 일치하는 문자가 무엇인지에 대한 "메모리"를 필요로하며 정규 표현식은 그것을 가지고 있지 않습니다. 즉, PCRE 스타일 일치가 수행 할 수있는 역 참조 및 비정규 표현식으로 수행 할 수 있습니다.
Bruce Ediger

3
@BruceEdiger 언어 (26)에 문자 수가 유한하고 문자열 (10)에 문자가있는 한, 가능합니다. 그것은 단지 많은 국가이지만 일반 언어가 아닌 것은 아닙니다.

1
"모든 영어 단어 ..."를 의미합니까? 하이픈과 아포스트로피가있는 철자를 포함하거나 포함하지 않는 것을 의미합니까? café, naïve, façade와 같은 단어를 포함 하시겠습니까?
hippietrail

답변:


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'

tr단어 s가 아닌 문자 ( c알파벳과 밑줄로 채워짐)를 개행 문자 로 변환하여 단어를 한 줄에 넣습니다 .

또는 하나 grep:

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

(10 자 미만 및 10 자 이상의 행 및 문자가 두 번 이상 나타나는 행은 제외).

grep가지만 (PCRE 지원 또는을 사용하는 GNU grep pcregrep) :

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

즉, 단어 경계 ( \b) 다음에 10 개의 단어 문자 시퀀스가 ​​나옵니다 (각각의 단어 뒤에 문자 사전 시퀀스 PCRE 연산자를 사용하여 단어 문자 시퀀스 자체가 뒤 따르지 않는 경우 (?!...)).

많은 정규 표현식 엔진이 반복 부품 내부에서 역 참조로 작동하지 않기 때문에 여기에서 작동하는 것이 운이 좋습니다.

(최소한 GNU grep 버전에서는)

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

작동하지 않지만

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

(AS 않는 echo aa | grep -Pw '(.)\2'벌레처럼 들린다).

당신은 원할 수 있습니다 :

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

ASCII가 아닌 로케일의 ASCII 문자뿐만 아니라 문자를 단어 구성 요소로 사용 \w하거나 \b고려 하려는 경우 .

다른 대안 :

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

그것은 단어 경계 (하나는 반복되는 일련의 단어 문자가 뒤 따르지 않는)와 10 개의 단어 문자입니다.

마음의 뒤에 할 것들 :

  • 비교는 대소 문자를 구분하므로 소문자와 대문자 Babylonish가 모두 2 개 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일치하지 않습니다 -.
Graeme

@Stephane 마지막 두 표현에 대한 간단한 설명을 게시 할 수 있습니까?
mkc

때로는 둘러보기가 RE로는 불가능했던 모든 것에 대한 해결책 인 것처럼 보입니다.
Barmar

1
@Barmar 정규식으로는 여전히 불가능합니다. "정규 표현식"은 리터럴 문자, 문자 클래스 및 '|', '(...)', '?', '+'및 '*'연산자와 같은 특정 구문 만 명시 적으로 허용하는 수학 구문입니다. 상기 중 하나가 아닌 연산자를 사용하는 소위 "정규 표현식"은 실제로 정규 표현식이 아닙니다.
Jules

1
@Jules 이것은 math.stackexchange.com이 아니라 unix.stackexchange.com입니다. 수학적 RE는 이러한 맥락에서 무의미합니다. 우리는 grep, PCRE 등에 사용하는 RE의 종류에 대해 이야기하고 있습니다.
Barmar

12

알았어 ... 여기 5 개의 문자열을 만드는 이상한 방법이 있습니다.

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

당신은 문자 클래스 (예에서 역 참조를 넣을 수 없기 때문에 [^\1|\2]), 당신은 사용해야합니다 예견 음을 - (?!foo). 이것은 PCRE 기능이므로 -P스위치 가 필요합니다 .

물론 10 자 문자열의 패턴은 훨씬 길지만, lookahead에서 가변 길이 ( '. *')를 사용하는 방법이 더 짧습니다.

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

Stephane Chazelas의 깨달은 대답을 읽은 후 grep의 -v스위치 를 통해 사용할 수있는 간단한 패턴이 있음을 깨달았습니다 .

    (.).*\1

검사는 한 번에 한 문자 씩 진행되므로, 주어진 문자 뒤에 0 개 이상의 문자 ( .*)가 오는지, 그리고 역 참조와 일치 하는지 확인합니다 . 이 패턴과 일치 하지 않는-v 것만 인쇄하여 뒤집 습니다. 이렇게하면 백 클래스 참조를 문자 클래스로 부정 할 수 없으므로 크게 유용합니다.

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

고유 한 문자로 모든 길이의 문자열을 식별하는 데 도움이되지만 다음과 같습니다.

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

고유 한 문자가있는 접미사와 abcabc일치하므로 (예 : abc끝으로 aaaa인해 및 끝으로 인해 a-따라서 문자열) 일치 하지 않습니다 . 이것은 너비가 0 너비 인 경우 발생하는 합병증입니다 (아무것도 소비하지 않음).


잘 했어! 이것은 Q의 것과 결합하여 작동합니다.
Graeme

1
나는 당신의 정규식 엔진은 가변 길이의 음을 내다을 허용하는 경우 첫 번째 하나를 간소화 할 수 있습니다 생각 :(.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!\4).
크리스토퍼 Creutzig

@ChristopherCreutzig : 물론 좋은 전화입니다. 내가 추가했습니다.
goldilocks

6

정규식에서 전체 작업을 수행 할 필요가 없다면 두 단계로 수행합니다. 먼저 10 자 단어를 모두 일치시킨 다음 고유성을 필터링합니다. 이 작업을 수행하는 방법을 아는 가장 짧은 방법은 Perl입니다.

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

\W정확히 10 자 길이의 단어 만 일치하도록 추가 앵커를 참고하십시오 .


감사합니다,하지만 난 : 정규식 oneliner로 싶습니다
딜런 Meeus에게

4

다른 사람들은 실제로 정규적이지 않은 특정 정규 표현식 시스템에 대한 다양한 확장 없이는 이것이 불가능하다고 제안했습니다. 그러나 일치시키려는 언어는 유한하기 때문에 분명히 규칙적입니다. 4 글자 알파벳 3 글자의 경우 다음과 같이 쉽습니다.

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

분명히 이것은 더 많은 글자와 더 큰 알파벳으로 서둘러 나옵니다. :-)


실제로 작동하는 답변이기 때문에 이것을 공표해야했습니다. 실제로 정규 표현식을 작성한 사람 중 가장 효율적이지 않을 수도 있지만 : P
Dylan Meeus

4

GNU의 옵션 --perl-regexp(short -P)은 grep미리 패턴을 포함하는보다 강력한 정규식을 사용합니다. 다음 패턴은이 단어가 나머지 단어에 나타나지 않는 각 문자를 찾습니다.

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

그러나 런타임 동작은 \w*거의 무한한 길이를 가질 수 있기 때문에 상당히 나쁩니다 . 으로 제한 될 수 \w{,8}있지만 단어 한도 인 10자를 초과합니다. 따라서 다음 패턴은 먼저 올바른 단어 길이를 확인합니다.

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

테스트 파일로 큰 MB 500MB 파일을 사용했습니다.

  • 첫 번째 패턴 : ≈ 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 버그를 해결하는 데 유용 합니다.
Stéphane Chazelas

@StephaneChazelas : 피드백 주셔서 감사합니다. 또한 탐욕스럽고 소유주의적인 운영자를 시도했지만 런타임 동작 (버전 2.13)에서 큰 변화가 발견되지 않았습니다. 버전 2.18이 훨씬 빠르며 최소한 약간의 개선이있었습니다. GNU grep 버그는 두 버전 모두에 있습니다. 어쨌든 상대 참조를 선호합니다 \g{-1}. 패턴에서 위치에 대해 더 독립적이기 때문입니다. 이 형식에서는 더 큰 패턴의 일부로 사용할 수 있습니다.
Heiko Oberdiek

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로 테스트


첫 번째와 세 번째는 아무 것도 수행하지 않으며, 두 번째는 연속 된 공백이 2 개 이상인 10 개 이상의 문자를 출력합니다. pastebin.com/eEDcy02D
manatwork

아마도 펄 버전 일 것이다. v5.14.2 및 v5.18.2로 테스트

Linux에서는 v5.14.1, Cygwin에서는 v5.14.2로 시도했습니다. 둘 다 앞에서 연결 한 pastebin 샘플에서와 같이 동작했습니다.
manatwork

첫 번째 줄은 Perl의 알려진 버전에서 작동합니다. 두 후자는 효과가 동일하지만 그렇지 않기 때문에 작동해야합니다. 욕심 많은 표현은 종종 욕심 많은 표현이 매우 실험적이라는 점에 주목하십시오.

최신 업데이트로 다시 테스트했습니다. 두 번째 것만 올바르게 출력됩니다. (그러나 단어는 한 줄로만 이루어져야하지만 문제는 전체 줄이 아니라 일치하는 단어에 관한 것입니다.)
manatwork
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.