10 자 길이의 모든 단어를 표시하는 정규 표현식을 작성하려고하는데 문자가 반복되지 않습니다.
지금까지 나는
grep --colour -Eow '(\w{10})'
질문의 첫 부분입니다. "고유성"을 확인하는 방법은 무엇입니까? 역 참조를 사용해야한다는 점을 제외하고는 실마리가 없습니다.
10 자 길이의 모든 단어를 표시하는 정규 표현식을 작성하려고하는데 문자가 반복되지 않습니다.
지금까지 나는
grep --colour -Eow '(\w{10})'
질문의 첫 부분입니다. "고유성"을 확인하는 방법은 무엇입니까? 역 참조를 사용해야한다는 점을 제외하고는 실마리가 없습니다.
답변:
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
와 악센트 사이에 단어 경계 가 있음)가 있기 때문에 일치 합니다.\w
일치하지 않습니다 -
.
알았어 ... 여기 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 너비 인 경우 발생하는 합병증입니다 (아무것도 소비하지 않음).
(.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!\4).
정규식에서 전체 작업을 수행 할 필요가 없다면 두 단계로 수행합니다. 먼저 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 자 길이의 단어 만 일치하도록 추가 앵커를 참고하십시오 .
다른 사람들은 실제로 정규적이지 않은 특정 정규 표현식 시스템에 대한 다양한 확장 없이는 이것이 불가능하다고 제안했습니다. 그러나 일치시키려는 언어는 유한하기 때문에 분명히 규칙적입니다. 4 글자 알파벳 3 글자의 경우 다음과 같이 쉽습니다.
(abc|abd|acb|acd|bac|bad|bcd|bdc|cab|cad|cbd|cdb|dab|dac|dbc|dcb)
분명히 이것은 더 많은 글자와 더 큰 알파벳으로 서둘러 나옵니다. :-)
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 파일을 사용했습니다.
최신 정보:
탐욕스럽지 않은 연산자 ( \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 버그를 해결하는 데 유용 합니다.
\g{-1}
. 패턴에서 위치에 대해 더 독립적이기 때문입니다. 이 형식에서는 더 큰 패턴의 일부로 사용할 수 있습니다.
펄 솔루션 :
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로 테스트