단어를 포함하지 않는 행과 일치하는 정규식


4292

단어를 일치시킨 다음 다른 도구 (예 :)를 사용하여 일치를 되돌릴 수 있다는 것을 알고 있습니다 grep -v. 그러나 hede정규 단어 를 사용하여 특정 단어를 포함하지 않는 행을 일치시킬 수 있습니까?

입력:

hoho
hihi
haha
hede

암호:

grep "<Regex for 'doesn't contain hede'>" input

원하는 출력 :

hoho
hihi
haha

84
아마 몇 년 늦었지만 무엇이 잘못 ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*되었습니까? 아이디어는 간단하다. 원하지 않는 문자열이 시작될 때까지 일치를 유지 한 다음 문자열이 완료되지 않은 N-1 경우 (N은 문자열 길이)에서만 일치합니다. 이러한 N-1 사례는 "h 다음에 non-e", "he 다음에 non-d"및 "hed 뒤에 non-e"입니다. 이러한 N-1 케이스를 전달하는 관리하는 경우, 당신은 성공적으로 하지 않았다 당신이 찾고 시작할 수 있도록 원치 않는 문자열과 일치 [^h]*다시
stevendesu

323
@stevendesu : '매우 긴 단어'나 더 나은 반 문장으로 이것을 시도하십시오. 타이핑 재미있게 보내십시오. BTW, 거의 읽을 수 없습니다. 성능 영향에 대해 모릅니다.
Peter Schuetze

13
@PeterSchuetze : 매우 긴 단어는 아니지만 실제로 실행 가능한 올바른 솔루션입니다. 성능에 대한 테스트를 실행하지는 않았지만 h (또는 단어의 첫 글자, 문장 등)를 볼 때까지 후자의 규칙이 대부분 무시되므로 너무 느리지 않을 것입니다. 그리고 반복 연결을 사용하여 긴 문자열에 대한 정규식 문자열을 쉽게 생성 할 수 있습니다. 작동하고 빠르게 생성 될 수 있다면 가독성이 중요합니까? 이것이 바로 의견입니다.
stevendesu

57
@stevendesu : 나도 나중에 있지만 그 대답은 거의 완전히 잘못되었습니다. 한 가지 이유로, "특정 단어를 포함하지 않는 행 일치"태스크가 주어지면 주제에 "h"를 포함하지 않아도됩니다. 내부 그룹을 선택적으로 만들고 패턴이 고정되었다고 가정합니다. ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ "hede"의 인스턴스 앞에 "hhede"와 같은 "hede"의 부분 인스턴스가 있으면 실패합니다.
jaytea

8
이 질문은 "Advanced Regex-Fu"아래 의 Stack Overflow Regular Expression FAQ 에 추가되었습니다 .
aliteralmind

답변:


5892

정규 표현식이 역 매칭을 지원하지 않는다는 개념은 전적으로 사실이 아닙니다. 부정적인 둘러보기를 사용하여이 동작을 모방 할 수 있습니다.

^((?!hede).)*$

위의 정규 표현식 은 (하위) 문자열 'hede'를 포함 하지 않는 모든 문자열 또는 줄 바꿈이없는 행과 일치합니다 . 언급 한 바와 같이,이 정규식에서 "좋은"(또는해야 할) 것이 아닙니다,하지만 여전히, 그것은 이다 가능합니다.

또한 줄 바꿈 문자도 일치시켜야하는 경우 DOT-ALL 수정자를 사용하십시오 ( s다음 패턴 의 후행 ).

/^((?!hede).)*$/s

또는 인라인으로 사용하십시오.

/(?s)^((?!hede).)*$/

(여기서 /.../정규 표현식 구분 기호는 패턴의 일부가 아닙니다)

DOT-ALL 수정자를 사용할 수없는 경우 문자 클래스를 사용하여 동일한 동작을 모방 할 수 있습니다 [\s\S].

/^((?!hede)[\s\S])*$/

설명

문자열은 n문자 목록 일뿐 입니다. 각 문자 전후에 빈 문자열이 있습니다. 따라서 n문자 목록 에는 n+1빈 문자열이 있습니다. 문자열을 고려하십시오 "ABhedeCD".

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = e1 A e2 B e3 h e4 e e5 d e6 e e7 C e8 D e9
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

여기서 e의는 빈 문자열입니다. 정규 표현식 (?!hede).은 하위 문자열이 없는지 미리 "hede"보고, 그 경우 (다른 것이 보이면) .(점)은 줄 바꿈을 제외한 모든 문자와 일치합니다. 둘러보기는 문자를 소비 하지 않기 때문에 폭이 0 인 어설 션 이라고도 합니다 . 그들은 단지 무언가를 주장하고 / 확인한다.

따라서 내 예에서 모든 빈 문자열은 먼저 "hede"문자가 .(점)에 의해 소비되기 전에 선행 이 없는지 확인합니다 . 정규 표현식 (?!hede).은 한 번만 수행하므로 그룹으로 묶여 0 회 이상 반복됩니다 ((?!hede).)*. 마지막으로 입력 시작과 끝은 전체 입력이 소비되도록 고정됩니다.^((?!hede).)*$

당신이 볼 수 있듯이, 입력이 "ABhedeCD"때문에 실패합니다 e3정규식, (?!hede)실패 (가 이다 "hede" 앞두고!).


26
나는 이것이 정규식이 나쁜 것이라고 말하지 않을 것입니다. 이 솔루션의 편리함은 매우 명백하며 프로그래밍 방식 검색과 비교할 때 성능 저하는 종종 중요하지 않습니다.
Archimaredes

29
엄밀히 말하면 부정적인 전조로 인해 정규 표현식이 규칙적이지 않습니다.
Peter K

55
@PeterK는 물론 MathOverflow 또는 CS-Stackexchange가 아니라 SO입니다. 여기에 질문을하는 사람들은 일반적으로 실용적인 답변을 찾고 있습니다. grep정규 표현식을 지원하는 대부분의 라이브러리 또는 도구 (예 : OP가 언급 한 것처럼 )에는 이론적으로 비정규적인 기능이 있습니다.
Bart Kiers

19
@Bart Kiers, 당신에게 대답하지 마십시오.이 용어의 남용은 저를 조금 자극합니다. 여기서 가장 혼란스러운 부분은 엄격한 의미의 정규 표현식이 OP가 원하는 것을 매우 많이 수행 할 수 있지만 일반적인 언어는 그것을 허용하지 않으므로 Look-Aheads와 같은 (수학적으로 추악한) 해결 방법이 있습니다. 아래의 대답 과 올바른 이론적 방법에 대한 나의 의견을 참조하십시오 . 말할 필요도없이 큰 입력에서 더 빠르게 작동합니다.
Peter K

17
vim에서이 작업을 수행하는 방법에 대해 궁금한 경우 :^\(\(hede\)\@!.\)*$
baldrs

738

해결책 "hede"로 시작 하지 않습니다 .

^(?!hede).*$

"hede"를 포함 하지 않는 솔루션보다 일반적으로 훨씬 효율적입니다 .

^((?!hede).)*$

전자는 모든 위치가 아닌 입력 문자열의 첫 번째 위치에서만 "hede"를 확인합니다.


5
감사합니다. 문자열에 숫자가 포함되어 있지 않은지 확인하는 데 사용했습니다 ^ ((?! \ d {5,}). *
Samih A

2
여보세요! 내가 작성 할 수 없습니다 종료 "HEDE"로 정규 표현식. 도와 드릴까요?
Aleks Ya

1
@AleksYa : "contain"버전을 사용하고 검색 문자열에 끝 앵커를 포함하십시오 : "hede"에서 "hede $"로 문자열을 "일치하지 않음"으로 변경
Nyerguds

2
@ AleksYa : 다음과 같이 부정적인 lookbehind를 사용하여 버전을 끝내지 않습니다 (.*)(?<!hede)$. @Nyerguds의 버전도 잘 작동하지만 답변에서 언급 한 성능의 요점을 완전히 놓치게됩니다.
thisismydesign

5
왜 그렇게 많은 답변을 말하는가 ^((?!hede).)*$? 사용하는 것이 더 효율적이지 ^(?!.*hede).*$않습니까? 이 같은 일을하지만 적은 단계
JackPRead

208

경우 그냥 그렙 위해 그것을 사용하고, 당신이 사용할 수있는 grep -v hedeHEDE를 포함하지 않는 모든 라인을 얻을 수 있습니다.

ETA 오, 질문을 다시 grep -v읽어 보면 "도구 옵션"이라는 의미 일 것입니다.


22
팁 : 원하지 않는 것을 점진적으로 필터링하려면 : grep -v "hede"| grep -v "hihi"| ...기타.
Olivier Lalonde

51
또는 하나의 프로세스 만 사용grep -v -e hede -e hihi -e ...
Olaf Dietsche

15
아니면 그냥 grep -v "hede\|hihi":)
Putnik

2
필터링하려는 패턴이 많은 경우 파일에 넣고 다음을 사용하십시오.grep -vf pattern_file file
codeforester

4
또는 간단 egrep하거나 grep -Ev "hede|hihi|etc"어색한 탈출을 피하기 위해.
Amit Naidu

160

대답:

^((?!hede).)*$

설명:

^문자열의 시작, (그룹화 및 \ 1까지 캡처 (0 회 이상 (가장 많은 양과 일치))
(?!없는지 확인하십시오.

hede 당신의 줄,

)미리보기 끝, .\ n을 제외한 모든 문자,
)*\ 1 끝 (참고 :이 캡처에서 수량 자를 사용하므로 캡처 된 패턴의 마지막 반복 만 \ 1에 저장 됨)
$옵션 \ n, 그리고 문자열의 끝


14
여러 단어 ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'를 사용하여 숭고한 텍스트 2에서 나를 위해 일한 멋진
Damodar Bashyal

3
@DamodarBashyal 늦어서 여기 꽤있어 알아,하지만 당신은 완전히 두 번째 임기가를 제거 할 수 있습니다 당신은 동일한 결과를 얻을 것이다
forresthopkinsa

99

주어진 대답은 학문적 요점으로 완벽하게 좋습니다.

이론적 컴퓨터 과학의 의미에서 정규 표현식은 할 수없는 다음과 같이 해. 그들에게는 다음과 같이 보일 것입니다.

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

이것은 전체 일치 만 수행합니다. 하위 경기를 위해 그것을하는 것은 더 어색 할 것입니다.


1
여기서는 기본 POSIX.2 정규식 만 사용하므로 PCRE를 사용할 수 없을 때 이식성이 더 뛰어납니다.
Steve-o

5
동의한다. 대부분의 정규 표현식은 정규 언어가 아니며 유한 오토마타로 인식 할 수 없습니다.
ThomasMcLeod

@ThomasMcLeod, Hades32 : 대답 '할 수 있도록 가능한 모든 정규 언어의 영역 내에서인가 하지 '와 ' '뿐만 아니라 '으로 또는 '식의 등 '과 같은 (hede|Hihi)'? (이것은 CS에 대한 질문 일 수 있습니다.)
James Haigh

7
@JohnAllen : ME !!! … 실제 정규 표현식이 아니라 학문적 참조이기도하며 계산 복잡성과 밀접한 관련이 있습니다. PCRE는 기본적으로 POSIX 정규식과 동일한 효율성을 보장 할 수 없습니다.
James Haigh 2016 년

4
죄송합니다-이 답변은 작동하지 않습니다, 그것은 hhehe와 일치하고 심지어 hehe와 부분적으로 (후반) 일치합니다
Falco

60

전체 문자열이 일치 하는 경우 에만 정규식 테스트가 실패 하도록하려면 다음이 작동합니다.

^(?!hede$).*

예 :- "foo"를 제외한 모든 값을 허용하려면 (예 : "foofoo", "barfoo"및 "foobar"는 통과하지만 "foo"는 실패 함) 다음을 사용하십시오. ^(?!foo$).*

물론, 당신이 정확한 동등성을 검사한다면 ,이 경우에 더 나은 일반적인 해결책은 문자열 동등성을 검사하는 것입니다.

myStr !== 'foo'

정규식 기능이 필요한 경우 테스트 외부 에서 부정을 넣을 수도 있습니다 (여기서는 대소 문자를 구분하지 않고 범위를 일치시킵니다).

!/^[a-f]oo$/i.test(myStr)

그러나이 답변의 맨 위에있는 정규식 솔루션은 긍정적 정규식 테스트가 필요한 경우 (아마도 API에 의해) 도움이 될 수 있습니다.


후행 공백은 어떻습니까? 예를 들어, 문자열로 테스트를 실패하려면 " hede "?
eagor

@eagor \s지시문은 단일 공백 ​​문자와 일치합니다
Roy Tinker

고맙지 만이 작업을 수행하기 위해 정규식을 업데이트하지 못했습니다.
eagor

2
@eagor :^(?!\s*hede\s*$).*
Roy Tinker

52

FWIW는 정규 언어 (일명 합리적 언어)가 보완 아래 닫히므로 항상 다른 표현을 무효화하는 정규식 (일명 합리적인 표현)을 찾을 수 있습니다. 그러나이를 구현하는 도구는 많지 않습니다.

Vcsn 은이 연산자 ( {c}후위를 나타냄 )를 지원합니다.

먼저 표현의 유형을 정의 라벨은 편지 ( lal_char) 선택하는 제품들 a에 대한 z예를 들어 (알파벳을 정의 보완 작업을 할 때, 물론 매우 중요합니다), 각 단어에 대해 계산 된 "값이"단지 부울입니다 : true단어가 수락 false되고 거부됩니다.

파이썬에서 :

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}  𝔹

그런 다음 식을 입력하십시오.

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

이 표현식을 자동으로 변환하십시오.

In [7]: a = e.automaton(); a

해당 오토 마톤

마지막으로,이 오토 마톤을 간단한 표현으로 다시 변환하십시오.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

여기서 +일반적으로 표시되고 |, \e비어있는 단어를 의미하며, [^]일반적으로 기록된다 .(모든 문자). 약간의 재 작성으로 ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

이 예제를 볼 수 있습니다 여기에 , 온라인 Vcsn 시도 .


6
사실이지만 추악하고 작은 문자 집합에만 사용할 수 있습니다. 당신은 :-) 유니 코드 문자열이하고 싶지 않아
reinierpost

가장 인상적인 Ragel 중 하나 인 더 많은 도구가 있습니다. 시작 정렬 된 경기의 경우 (any *-( 'hehe'any *))로, 정렬되지 않은 경우 (any *-( 'hehe'any *))로 작성됩니다.
Peter K

1
@ reinierpost : 왜 추악하고 유니 코드의 문제는 무엇입니까? 나는 둘 다에 동의 할 수 없습니다. (vcsn에는 경험이 없지만 DFA에는 경험이 없습니다).
피터 K

3
@PedroGimeno 당신이 정박했을 때, 당신은이 정규 표현식을 먼저 괄호 안에 넣었습니까? 그렇지 않으면 앵커 사이의 우선 순위와 |잘 재생되지 않습니다. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
akim

1
이 방법은 OP가 요청한 단어 'hede'를 포함하지 않고 'hede'라는 단어 가 아닌 행을 일치시키는 것 입니다. 후자의 내 대답을 참조하십시오.
Pedro Gimeno

51

여기 에 임의의 정규식을 부정하는 것이 쉽지 않은 이유에 대한 좋은 설명 이 있습니다. 그러나 다른 대답에 동의해야합니다. 이것이 가상의 질문 이외의 것이면 정규 표현식이 올바른 선택이 아닙니다.


10
일부 도구, 특히 mysqldumpslow는 데이터를 필터링하는이 방법 만 제공하므로 이러한 경우 정규식을 찾는 것이 도구를 다시 쓰는 것 외에는 최상의 솔루션입니다 (여러 패치는 MySQL AB / Sun에 포함되지 않았습니다) / Oracle
FGM

1
내 상황에 정확히 맞아 Velocity 템플릿 엔진은 정규식을 사용하여 변환을 적용 할시기 (escape html)를 결정하며 한 상황에서 항상 EXCEPT를 제외하고 작동하기를 원합니다.
Henno Vermeulen

1
어떤 대안이 있습니까? 정규식 외에 정확한 문자열 일치를 수행 할 수있는 것을 본 적이 없습니다. OP가 프로그래밍 언어를 사용하는 경우 다른 도구를 사용할 수 있지만 코드 작성을 사용하지 않는 경우 다른 선택이 없을 수 있습니다.
kingfrito_5005

2
정규 표현식이 가장 적합한 선택이 될 수있는 많은 가상의 시나리오 중 하나입니다. 로그 출력을 보여주는 IDE (Android Studio)에 있으며 제공된 필터링 도구는 일반 문자열 및 정규 표현식입니다. 일반 문자열 로이 작업을 시도하면 완전히 실패합니다.
LarsH

48

부정적 예측을 사용하면 정규식은 특정 패턴을 포함하지 않는 것과 일치 할 수 있습니다. 이것은 Bart Kiers가 대답하고 설명합니다. 좋은 설명!

그러나 Bart Kiers의 답변으로 lookahead 부분은 단일 문자와 일치하면서 1 ~ 4자를 미리 테스트합니다. 이를 피하고 lookahead 부분이 전체 텍스트를 확인하고 'hede'가 없는지 확인한 다음 일반 부분 (. *)이 전체 텍스트를 한 번에 모두 먹을 수 있습니다.

향상된 정규식은 다음과 같습니다.

/^(?!.*?hede).*$/

네거티브 lookahead 부분의 (*?) 게으른 수량자는 선택 사항입니다. 데이터에 따라 대신 (*) 욕심 많은 수량자를 사용할 수 있습니다. 'hede'가 있고 텍스트의 절반에 게으른 수량자가있을 경우 더 빠르다. 그렇지 않으면 탐욕스러운 수량자가 더 빠릅니다. 그러나 'hede'가 없으면 둘 다 느려집니다.

데모 코드 는 다음과 같습니다 .

lookahead에 대한 자세한 내용은 위대한 기사 인 Lookahead 및 Lookbehind 마스터 링을 확인하십시오 .

또한 복잡한 정규식을 생성하는 데 도움이되는 JavaScript 정규식 생성기 인 RegexGen.js 를 확인하십시오 . RegexGen.js를 사용하면보다 읽기 쉬운 방식으로 정규식을 구성 할 수 있습니다.

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

3
주어진 문자열에 str1과 str2가 포함되어 있지 않은지 간단히 확인하려면 :^(?!.*(str1|str2)).*$
S.Serpooshan

1
또는 ^(?!.*?(?:str1|str2)).*$데이터에 따라 게으른 수량자를 사용할 수 있습니다 . ?:캡처 할 필요가 없기 때문에를 추가 했습니다.
amobiz

이것은 지금까지 10xms의 가장 좋은 대답입니다. jsfiddle 코드를 추가하면 답변에 결과가 표시 될 수 있습니다. 허덕이 없을 때 게으른 버전이 욕심 많은 버전보다 빠른 이유가 궁금합니다. 그들은 같은 시간이 걸리지 않아야합니까?
user5389726598465

예, 그들은 전체 텍스트를 테스트하기 때문에 같은 시간이 걸립니다.
amobiz

41

벤치 마크

제시된 옵션 중 일부를 평가하고 성능을 비교하고 새로운 기능을 사용하기로 결정했습니다. .NET Regex Engine 벤치마킹 : http://regexhero.net/tester/

벤치 마크 텍스트 :

처음 7 줄은 검색된 Expression을 포함하므로 일치하지 않아야합니다. 반면 7 줄은 일치해야합니다!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

결과 :

- 결과는 3 개 실행의 중간으로 초당 반복을하다 더 큰 수 = 더 나은

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

.NET은 동작 동사 (* FAIL 등)를 지원하지 않으므로 솔루션 P1 및 P2를 테스트 할 수 없습니다.

요약:

가장 제안 된 솔루션을 테스트하려고했지만 특정 단어에 대해 일부 최적화가 가능합니다. 예를 들어, 검색 문자열의 처음 두 글자가 같지 않으면 답변 03을 확장하여 ^(?>[^R]+|R+(?!egex Hero))*$성능이 약간 향상 될 수 있습니다 .

그러나 전반적으로 가장 읽기 쉽고 성능 측면에서 가장 빠른 솔루션은 조건문을 사용하여 05 또는 소유 수량자를 사용하여 04로 보입니다. Perl 솔루션은 훨씬 빠르고 쉽게 읽을 수 있어야한다고 생각합니다.


5
당신도 시간을 보내야합니다 ^(?!.*hede). /// 또한, 일치하는 말뭉치와 일치하지 않는 말뭉치에 대한 식의 순위를 따로 지정하는 것이 좋습니다. 왜냐하면 대부분의 줄 일치 또는 대부분의 줄이 그렇지 않은 경우입니다.
ikegami

32

정규 표현식은 아니지만 파이프와 함께 직렬 그렙을 사용하여 소음을 제거하는 것이 논리적이고 유용하다는 것을 알았습니다.

예. 모든 주석없이 아파치 구성 파일을 검색하십시오.

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

직렬 grep의 논리는 (댓글이 아님) 및 (dir과 일치)


2
나는 그가의 정규식 버전을 요청하는 생각grep -v
Angel.King.47

9
이것은 위험합니다. 또한 같은 라인을 그리워good_stuff #comment_stuff
Xavi Montero

29

이를 통해 각 위치에서 미리보기를 테스트하지 않아도됩니다.

/^(?:[^h]+|h++(?!ede))*+$/

(.net의 경우)에 해당 :

^(?>(?:[^h]+|h+(?!ede))*)$

이전 답변 :

/^(?>[^h]+|h+(?!ede))*$/

7
좋은 지적; 나는 아무도이 방법을 전에 언급하지 않은 것에 놀랐다. 그러나 해당 정규 표현식은 일치하지 않는 텍스트에 적용될 때 역 추적 하기 쉽습니다 . 방법은 다음과 같습니다./^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore

... 또는 모든 수량자를 소유 할 수 있습니다. ;)
Alan Moore

@ 앨런 무어-나도 놀랐어. 아래 답변에 동일한 패턴을 게시 한 후에 만 ​​귀하의 의견 (및 더미에서 최고의 정규 표현식)을 보았습니다.
ridgerunner

@ridgerunner는 최고의 도일 필요는 없습니다. 최고의 답변이 더 나은 벤치 마크를 보았습니다. (나는 그 일에 놀랐다.)
Qtax

23

위에 (?:(?!hede).)*언급 한 것은 고정 될 수 있기 때문에 훌륭합니다.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

그러나이 경우에는 다음과 같이 충분합니다.

^(?!.*hede)                    # A line without hede

이 단순화는 "AND"절을 추가 할 준비가되었습니다.

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

20

내가하는 방법은 다음과 같습니다.

^[^h]*(h(?!ede)[^h]*)*$

다른 답변보다 정확하고 효율적입니다. Friedl의 "언 롤링 루프 (unrolling-the-loop)" 효율 기술을 구현하며 역 추적이 훨씬 적습니다.


17

문자 클래스를 부정하는 것과 비슷한 단어를 부정하기 위해 문자를 일치 시키려면 :

예를 들어, 문자열 :

<?
$str="aaa        bbb4      aaa     bbb7";
?>

사용하지 마세요:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

사용하다:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

공지 사항 "(?!bbb).", 예를 들어도 lookbehind도 내다보기, 그것의 lookcurrent입니다 :

"(?=abc)abcde", "(?!abc)abcde"

3
perl regexp에는 "lookcurrent"가 없습니다. 이것은 실제로 부정적인 전망입니다 (접두사 (?!). 긍정적 인 lookahead의 접두사는 (?=같지만 해당 lookbehind 접두사는 각각 (?<!(?<=입니다. lookahead는 다음 문자 (“ahead”)를 소비하지 않고 읽는 것을 의미합니다. lookbehind는 이미 소비 된 문자를 확인한다는 의미입니다.
Didier L

14

내 의견으로는 더 많은 최고의 답변을 읽을 수 있습니다.

^(?!.*hede)

기본적으로 " 'hede'가없는 경우에만 줄의 시작과 일치하므로 요구 사항은 거의 직접 정규식으로 변환됩니다.

물론 여러 가지 실패 요구 사항이있을 수 있습니다.

^(?!.*(hede|hodo|hada))

세부 정보 : ^ 앵커는 정규식 엔진이 모든 문자열과 일치하는 문자열의 모든 위치에서 일치를 다시 시도하지 않도록합니다.

시작에서 ^ 앵커는 줄의 시작을 나타냅니다. grep 도구는 여러 줄 문자열로 작업하는 상황에서 "m"플래그를 사용할 수 있습니다.

/^(?!.*hede)/m # JavaScript syntax

또는

(?m)^(?!.*hede) # Inline flag

다중 부정에 대한 훌륭한 예.
Peter Parada

최고 답변과의 한 가지 차이점은 이것이 일치하지 않으며 "hede"가 없으면 전체 라인과 일치한다는 것입니다
.

13

OP는 TagRegex가 사용될 컨텍스트 (프로그래밍 언어, 편집기, 도구)를 나타 내기 위해 게시물을 지정하지 않았습니다 .

나를 위해을 사용하여 파일을 편집하는 동안 때로는이 작업을 수행해야합니다 Textpad.

Textpad 일부 정규 표현식을 지원하지만 미리보기 또는 뒤보기를 지원하지 않으므로 몇 가지 단계가 필요합니다.

string을 포함 하지 않는 모든 줄을 유지 하려면 hede다음과 같이하십시오.

1. 전체 파일을 검색 / 바꾸어 텍스트가 포함 된 각 줄의 시작 부분에 고유 한 "태그"를 추가하십시오.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. 문자열이 포함 된 모든 행을 삭제하십시오 hede(교체 문자열이 비어 있음).

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3.이 시점에서 나머지 모든 줄 은 문자열을 포함 하지 않습니다hede . 모든 줄에서 고유 한 "태그"를 제거하십시오 (대체 문자열이 비어 있음).

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

이제 문자열을 포함하는 모든 줄이 hede제거 된 원본 텍스트가 있습니다.


내가 찾고 있어요 경우 다른 것을 만 라인에 음주하지 문자열을 포함 hede,이 같은 그것을 할 것입니다 :

1. 전체 파일을 검색 / 바꾸어 텍스트가 포함 된 각 줄의 시작 부분에 고유 한 "태그"를 추가하십시오.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. 문자열이 포함 된 모든 행에 hede대해 고유 한 "태그"를 제거하십시오.

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3.이 시점에서 고유 한 "태그"로 시작하는 모든 행 은 문자열을 포함 하지 않습니다hede . 나는 이제 그 라인에만 다른 것을 할 수 있습니다 .

4. 완료되면 모든 줄에서 고유 한 "태그"를 제거합니다 (교체 문자열이 비어 있음).

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

12

다른 사람은 요청 된 질문에 대한 직접적인 대답을하지 않았으므로 할 것입니다.

대답은 POSIX grep를 사용하면 문자 그대로이 요청을 만족시킬 수 없다는 것입니다.

grep "<Regex for 'doesn't contain hede'>" input

그 이유는 POSIX grep기본 정규 표현식 으로 작업하는 데만 필요하기 때문에 해당 작업을 수행하기에 충분히 강력하지 않습니다 (대체 및 괄호가 없기 때문에 정규 언어를 구문 분석 할 수 없음).

그러나 GNU grep는이를 허용하는 확장을 구현합니다. 특히, \|BREs의 GNU의 구현에 교대 운영자이며, \(그리고 \)괄호입니다. 정규식 엔진이 교대, 음수 대괄호 식, 괄호 및 Kleene 별을 지원하고 문자열의 시작과 끝에 고정 할 수 있으면이 방법이 필요합니다. 그러나 음수 집합 [^ ... ]은 그 외에도 매우 편리합니다. 그렇지 않으면 (a|b|c| ... )집합에 포함되지 않은 모든 문자를 나열 하는 형식의 표현으로 대체해야하므로 매우 지루하고 지나치게 길며 훨씬 더 긴 경우 전체 문자 세트는 유니 코드입니다.

GNU grep의 경우 대답은 다음과 같습니다.

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input

( Grail 과 수작업으로 만든 추가 최적화 기능으로 발견 ).

또한 구현이있는 도구를 사용하여 정규 표현식을 확장 같은, egrep백 슬래시 없애 :

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input

테스트 할 스크립트는 다음과 같습니다 ( testinput.txt현재 디렉토리에 파일 을 생성 함 ).

#!/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)

내 시스템에서 다음을 인쇄합니다.

Files /dev/fd/63 and /dev/fd/62 are identical

예상대로.

세부 사항에 관심이있는 사람들을 위해 사용 된 기술은 단어와 일치하는 정규식을 유한 오토 마톤으로 변환 한 다음 모든 수락 상태를 비수용으로 변경하거나 그 반대로 오토 마톤을 반전시킨 다음 결과 FA를 정규식.

마지막으로, 모든 사람들이 지적했듯이 정규 표현식 엔진이 부정적인 예측을 지원하면 작업이 크게 단순화됩니다. 예를 들어 GNU grep을 사용하는 경우 :

grep -P '^((?!hede).)*$' input

업데이트 : 최근 Grad 와 유사한 기능을 제공하는 PHP로 작성된 Kendall Hopkins의 우수한 FormalTheory 라이브러리를 발견했습니다 . 그것을 사용하고 직접 작성 한 단순화를 사용하여 입력 문구 (현재 영숫자 및 공백 문자 만 지원됨)가 주어진 음수 정규 표현식의 온라인 생성기를 작성할 수있었습니다 : http://www.formauri.es/personal/ pgimeno / misc / 비 일치 정규식 /

hede그것을 위해 출력 :

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

이는 위와 같습니다.


11

루비 -2.4.1이 도입 된 이후, 루비 정규 표현식에서 새로운 부재 연산자 를 사용할 수 있습니다

공식 문서에서

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

따라서 당신의 경우 ^(?~hede)$에는 당신을 위해 일을합니까

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

9

PCRE 동사를 통해 (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

이것은 정확한 문자열을 포함 hede하고 나머지 모든 행과 일치 하는 행을 완전히 건너 뜁니다 .

데모

부품의 실행 :

위 정규식을 두 부분으로 나누어 고려해 봅시다.

  1. |기호 앞의 부분 . 부품 이 일치하지 않아야합니다 .

    ^hede$(*SKIP)(*F)
  2. |기호 뒤 부분 . 부분 이 일치해야합니다 .

    ^.*$

1 부

정규식 엔진은 첫 번째 부분부터 실행을 시작합니다.

^hede$(*SKIP)(*F)

설명:

  • ^ 우리가 시작에 있다고 주장한다.
  • hede 문자열과 일치 hede
  • $ 우리가 줄 끝에 있다고 주장한다.

따라서 문자열을 포함하는 줄 hede이 일치합니다. 정규식 엔진은 다음과 같은보고되면 (*SKIP)(*F)( :주의 당신은 쓸 수 (*F)(*FAIL) 동사가 생략)가하고 경기가 실패 할 수 있습니다. |PCRE 동사 옆에 추가 된 변경 또는 논리 OR 연산자는 행이 정확한 문자열을 포함하는 것을 제외하고 모든 행의 각 문자 사이에 존재합니다 hede. 여기 데모를 참조 하십시오 . 즉, 나머지 문자열의 문자를 일치 시키려고합니다. 이제 두 번째 부분의 정규식이 실행됩니다.

2 부

^.*$

설명:

  • ^ 우리가 시작에 있다고 주장한다. 즉, 행의 행을 제외하고 모든 행 시작과 일치합니다 hede. 여기 데모를 참조 하십시오 .
  • .*여러 .줄 모드에서 줄 바꿈 또는 캐리지 리턴 문자를 제외한 모든 문자를 찾습니다. 그리고 *이전 문자를 0 번 이상 반복합니다. 그래서 .*전체 라인을 일치합니다. 여기 데모를 참조 하십시오 .

    왜 + 대신에. *를 추가했는지?

    때문에이 .*빈 줄을 일치합니다하지만 .+빈 일치하지 않습니다. 우리는 제외한 모든 라인을 일치시킬 hede또한 입력에 빈 줄 가능성이있을 수 있습니다. 따라서 .*대신 사용해야합니다 .+. .+이전 문자를 한 번 이상 반복합니다. .*빈 줄 일치는 여기를 참조 하십시오 .

  • $ 여기서 라인 앵커의 끝은 필요하지 않습니다.


7

코드에서 두 개의 정규 표현식을 유지 관리하는 것이 더 좋을 수 있습니다. 하나는 첫 번째 일치를 수행 한 다음 일치하는 경우 두 번째 정규 표현식을 실행하여 이상 사례를 확인하고 예를 들어 차단하려는 경우 ^.*(hede).*코드에 적절한 논리를 갖습니다.

좋아, 이것은 실제로 게시 된 질문에 대한 답변이 아니며 단일 정규 표현식보다 약간 더 많은 처리를 사용할 수도 있음을 인정합니다. 그러나 이상 사례에 대한 빠른 긴급 수정을 찾고자하는 개발자에게는이 솔루션을 간과해서는 안됩니다.


5

TXR 언어는 정규식 부정을 지원합니다.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

더 복잡한 예 :로 시작하는 모든 라인을 일치 a로 끝 z하지만 문자열을 포함하지 않는를 hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

정규 표현식 부정은 그 자체로는 특히 유용하지는 않지만 교차가있을 때 부울 세트 연산 세트가 있기 때문에 상황이 흥미로워집니다. "일치하는 것을 제외하고는 이것과 일치하는 세트"를 표현할 수 있습니다.


ElasticSearch Lucene 기반 정규식에 대한 솔루션이기도합니다.
Wiktor Stribiżew

5

또 다른 옵션은 긍정적 인 예견을 추가 hehe하고 입력 행의 어느 곳에 있는지 확인하는 것입니다.

^(?!(?=.*\bhede\b)).*$

단어 경계와 함께.


표현식은 regex101.com의 오른쪽 상단에 설명되어 있습니다. 탐색 / 단순화 / 수정을 원하면 이 링크 에서 원하는 경우 일부 샘플 입력과 어떻게 일치하는지 볼 수 있습니다.


정규식 회로

jex.im은 정규 표현식을 시각화합니다.

여기에 이미지 설명을 입력하십시오


4

아래 기능은 원하는 출력을 얻는 데 도움이됩니다.

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

2

^ ((?! hede).) * $는 문자를 소비하기 때문에 다른 기준과 결합 할 수 없다는 점을 제외하고는 훌륭한 솔루션입니다. 예를 들어, "hede"가 없는지 "haha"가 있는지 확인하고 싶다고 가정하십시오. 이 솔루션은 문자를 소비하지 않기 때문에 작동합니다.

^ (?!. \ bhede \ b) (? =. \ bhaha \ b)


1

PCRE의 역 추적 제어 동사를 사용하여 단어를 포함하지 않는 행과 일치시키는 방법

이전에 사용하지 않은 방법은 다음과 같습니다.

/.*hede(*COMMIT)^|/

작동 원리

먼저, 어딘가에서 "hede"를 찾으려고 노력합니다. 성공하면이 시점 (*COMMIT)에서 엔진에 장애 발생시 역 추적뿐만 아니라 해당 경우 더 이상 일치하지 않도록 지시합니다. 그런 다음 일치하지 않는 항목 (이 경우 ^) 을 일치 시키려고합니다 .

행에 "hede"가 포함되어 있지 않으면 두 번째 대안 인 빈 하위 패턴이 제목 문자열과 성공적으로 일치합니다.

이 방법은 부정적 예측보다 효율적이지 않지만 누군가가 멋진 것을 발견하고 더 흥미로운 다른 응용 프로그램에서 사용하는 경우를 대비하여 여기에 던질 것이라고 생각했습니다.


0

더 간단한 해결책은 not 연산자를 사용하는 것입니다 !

귀하의 경우 문이 일치해야합니다 및 "제외"를 일치하지 "할 수 있습니다."

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

RegEx의 설계자는 연산자가 아닌 사람의 사용을 예상했습니다.


0

하위 문자열을 포함 하지 않는 한 줄의 세그먼트 (전체 줄이 아닌 )와 일치 할 수있는 정규 표현식을 작성하려고 시도하는 동안 Google에서 찾을 수 있습니다. 알아내는 데 시간이 걸렸으므로 공유하겠습니다.

주어진 문자열 : <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

<span>하위 문자열 "bad"를 포함하지 않는 태그 를 일치시키고 싶습니다 .

/<span(?:(?!bad).)*?>일치 <span class=\"good\"><span class=\"ugly\">.

괄호에는 두 세트 (계층)가 있습니다.

  • 가장 안쪽은 부정적 예측을위한 것입니다 (캡처 그룹이 아님).
  • 가장 바깥 쪽은 Ruby에서 캡처 그룹으로 해석되었지만 캡처 그룹이되기를 원하지 않으므로 시작시에? :를 추가하고 더 이상 캡처 그룹으로 해석되지 않습니다.

루비 데모 :

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

0

ConyEdit , 명령 줄을 사용하여 cc.gl !/hede/정규 표현식 매칭을 포함하거나 명령 줄을 사용하지 않는 라인을 얻을 cc.dl /hede/정규식 매칭을 포함 삭제 라인을. 그들은 같은 결과를 얻습니다.


0

문자열 X 가 포함되어 있지만 문자열 Y가 포함되지 않은 전체 줄을 일치시키려는 경우에 대한 다른 예를 추가하고 싶었습니다. .

예를 들어 URL / 문자열에 " tasty-treats " 가 포함되어 있는지 , 어디에서나 " chocolate "이 포함되어 있지 않은지 확인하고 싶다고 가정 해 보겠습니다 .

이 정규식 패턴은 작동합니다 (JavaScript에서도 작동합니다)

^(?=.*?tasty-treats)((?!chocolate).)*$

(예제에서 글로벌 멀티 라인 플래그)

대화식 예 : https://regexr.com/53gv4

성냥

(이 URL은 "맛있는 음식"을 포함하고 "초콜릿"도 포함하지 않습니다)

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

일치하지 않습니다

(이 URL에는 어딘가에 "초콜릿"이 포함되어 있으므로 "맛있는 음식"이 포함되어 있어도 일치하지 않습니다.)

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.