Eu sei que é possível combinar uma palavra e, em seguida, reverter as correspondências usando outras ferramentas (por exemplo grep -v
). No entanto, é possível combinar linhas que não contêm uma palavra específica, por exemplo hede
, usando uma expressão regular?
Entrada:
hoho
hihi
haha
hede
Código:
grep "<Regex for 'doesn't contain hede'>" input
Saída desejada:
hoho
hihi
haha
regex
regex-negation
knaser
fonte
fonte
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
:? A ideia é simples. Mantenha a correspondência até ver o início da sequência indesejada e, em seguida, corresponda apenas nos casos N-1 em que a sequência está inacabada (onde N é o comprimento da sequência). Esses casos N-1 são "h seguido por não-e", "ele seguido por não-d" e "hed seguido por não-e". Se você conseguiu passar estes N-1 dos casos, você com êxito não coincidir com a corda indesejado para que você possa começar a procurar[^h]*
novamente^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$
isso falha quando instâncias de "hede" são precedidas por instâncias parciais de "hede", como em "hhede".Respostas:
A noção de que o regex não suporta correspondência inversa não é totalmente verdadeira. Você pode imitar esse comportamento usando pesquisas negativas:
A regex acima corresponderá a qualquer sequência ou linha sem quebra de linha, que não contenha a (sub) sequência 'hede'. Como mencionado, isso não é algo que o regex seja "bom" em (ou deveria fazer), mas ainda assim, é possível.
E se você também precisar corresponder os caracteres de quebra de linha, use o modificador DOT-ALL (
s
o seguinte no padrão a seguir):ou use-o em linha:
(onde o
/.../
são os delimitadores de expressões regulares, ou seja, não fazem parte do padrão)Se o modificador DOT-ALL não estiver disponível, você poderá imitar o mesmo comportamento com a classe de caracteres
[\s\S]
:Explicação
Uma string é apenas uma lista de
n
caracteres. Antes e depois de cada personagem, há uma string vazia. Portanto, uma lista den
caracteres terán+1
cadeias vazias. Considere a string"ABhedeCD"
:Onde
e
estão as cordas vazias. O regex(?!hede).
olha para frente para ver se não há substring"hede"
a ser visto e, se esse for o caso (para que outra coisa seja vista), o.
(ponto) corresponderá a qualquer caractere, exceto uma quebra de linha. Look-arounds também são chamados de asserções de largura zero porque não consomem nenhum caractere. Eles apenas afirmam / validam algo.Portanto, no meu exemplo, toda string vazia é validada primeiro para ver se não há nenhuma
"hede"
adiante, antes que um caractere seja consumido pelo.
(ponto). A regex(?!hede).
vai fazer isso apenas uma vez, por isso é envolto em um grupo, e repetido zero ou mais vezes:((?!hede).)*
. Por fim, o início e o final da entrada são ancorados para garantir que toda a entrada seja consumida:^((?!hede).)*$
Como você pode ver, a entrada
"ABhedeCD"
irá falhar porque eme3
, a regex(?!hede)
falhar (não é"hede"
lá na frente!).fonte
grep
as mencionadas pelo OP) com suporte a regex têm recursos que as tornam não regulares em um sentido teórico.^\(\(hede\)\@!.\)*$
Observe que a solução para não começa com "hede" :
geralmente é muito mais eficiente do que a solução para não contém "hede" :
O primeiro verifica “hede” apenas na primeira posição da string de entrada, e não em todas as posições.
fonte
(.*)(?<!hede)$
. A versão do @Nyerguds também funcionaria, mas perde completamente o objetivo de desempenho mencionado pela resposta.^((?!hede).)*$
? Não é mais eficiente usar^(?!.*hede).*$
? Faz a mesma coisa, mas em menos etapasSe você está usando o grep, pode usar
grep -v hede
para obter todas as linhas que não contêm hede.ETA Oh, relendo a pergunta,
grep -v
provavelmente é o que você quis dizer com "opções de ferramentas".fonte
grep -v -e hede -e hihi -e ...
grep -v "hede\|hihi"
:)grep -vf pattern_file file
egrep
ougrep -Ev "hede|hihi|etc"
para evitar a fuga embaraçosa.Responda:
Explicação:
^
o início da sequência,(
agrupe e capture para \ 1 (0 ou mais vezes (correspondendo à maior quantidade possível)),(?!
observe se não há,hede
sua corda,)
final da observação,.
qualquer caractere, exceto \ n,)*
final de \ 1 (Observação: como você está usando um quantificador nessa captura, apenas a ÚLTIMA repetição do padrão capturado será armazenada em \ 1)$
antes de um \ n opcional, e o fim da cordafonte
^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$
'As respostas dadas são perfeitamente boas, apenas um ponto acadêmico:
Expressões regulares no significado de ciências da computação não são capazes de fazê-lo assim. Para eles, era algo parecido com isto:
Isso faz apenas uma correspondência COMPLETA. Fazer isso para sub-partidas seria ainda mais complicado.
fonte
(hede|Hihi)
'? (Isso talvez uma pergunta para CS.)Se você deseja que o teste regex falhe apenas se a sequência inteira corresponder, o seguinte funcionará:
por exemplo - Se você deseja permitir todos os valores, exceto "foo" (ou seja, "foofoo", "barfoo" e "foobar" serão aprovados, mas "foo" falhará), use:
^(?!foo$).*
Obviamente, se você estiver verificando a igualdade exata , uma solução geral melhor nesse caso é verificar a igualdade das strings, ou seja,
Você pode até colocar a negação fora do teste se precisar de recursos de regex (aqui, insensibilidade a maiúsculas e minúsculas):
A solução regex na parte superior desta resposta pode ser útil, no entanto, em situações em que é necessário um teste positivo de regex (talvez por uma API).
fonte
" hede "
?\s
directiva corresponde a um único espaço em branco^(?!\s*hede\s*$).*
FWIW, como as linguagens regulares (também conhecidas como linguagens racionais) são fechadas sob complementação, sempre é possível encontrar uma expressão regular (também conhecida como expressão racional) que nega outra expressão. Mas poucas ferramentas implementam isso.
O Vcsn suporta esse operador (que indica
{c}
, postfix).Você primeiro definir o tipo de suas expressões: as etiquetas são carta (
lal_char
) para escolher a partira
dez
, por exemplo (que define o alfabeto quando se trabalha com complementação é, naturalmente, muito importante), e o "valor" calculado para cada palavra é apenas um valor booleano :true
a palavra é aceita,false
, rejeitada.Em Python:
então você insere sua expressão:
converta esta expressão em um autômato:
finalmente, converta esse autômato em uma expressão simples.
onde
+
normalmente é indicado|
,\e
denota a palavra vazia e[^]
geralmente é escrito.
(qualquer caractere). Então, com um pouco de reescrita()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*
.Você pode ver este exemplo aqui e tente o Vcsn online lá .
fonte
|
não funcionarão muito bem.'^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'
.Aqui está uma boa explicação de por que não é fácil negar uma regex arbitrária. Porém, eu tenho que concordar com as outras respostas: se isso é outra coisa que não uma pergunta hipotética, uma regex não é a escolha certa aqui.
fonte
Com aparência negativa, a expressão regular pode corresponder a algo que não contém padrão específico. Isso é respondido e explicado por Bart Kiers. Ótima explicação!
No entanto, com a resposta de Bart Kiers, a parte do lookahead testará de 1 a 4 caracteres à frente, combinando com qualquer caractere único. Podemos evitar isso e deixar que a parte do lookah verifique o texto inteiro, verifique se não há 'hede' e, em seguida, a parte normal (. *) Pode comer o texto inteiro de uma só vez.
Aqui está o regex aprimorado:
Observe que o quantificador lento (*?) Na parte negativa do lookahead é opcional; você pode usar (*) quantificador ganancioso, dependendo dos seus dados: se 'hede' aparecer e na metade inicial do texto, o quantificador lento seja mais rápido; caso contrário, o quantificador ganancioso será mais rápido. No entanto, se 'hede' não estiver presente, ambos serão iguais lentamente.
Aqui está o código de demonstração .
Para obter mais informações sobre lookahead, confira o ótimo artigo: Mastering Lookahead and Lookbehind .
Além disso, consulte o RegexGen.js , um gerador de expressões regulares JavaScript que ajuda a construir expressões regulares complexas. Com o RegexGen.js, você pode construir o regex de uma maneira mais legível:
fonte
^(?!.*(str1|str2)).*$
^(?!.*?(?:str1|str2)).*$
dependendo dos seus dados. Adicionado o?:
desde que não precisamos capturá-lo.Benchmarks
Decidi avaliar algumas das opções apresentadas e comparar seu desempenho, além de usar alguns novos recursos. Benchmarking no .NET Regex Engine: http://regexhero.net/tester/
Texto de referência:
As primeiras 7 linhas não devem corresponder, pois contêm a expressão pesquisada, enquanto as 7 linhas inferiores devem corresponder!
Resultados:
Os resultados são iterações por segundo, com a mediana de 3 execuções - Maior número = melhor
Como o .NET não suporta verbos de ação (* FAIL, etc.), não pude testar as soluções P1 e P2.
Resumo:
Tentei testar a maioria das soluções propostas, algumas otimizações são possíveis para certas palavras. Por exemplo, se as duas primeiras letras da sequência de pesquisa não forem as mesmas, a resposta 03 poderá ser expandida para
^(?>[^R]+|R+(?!egex Hero))*$
resultar em um pequeno ganho de desempenho.Porém, a solução mais rápida e mais legível em termos de desempenho parece ser 05 usando uma declaração condicional ou 04 com o quantificador possível. Eu acho que as soluções Perl devem ser ainda mais rápidas e fáceis de ler.
fonte
^(?!.*hede)
também. /// Além disso, provavelmente é melhor classificar as expressões para o corpus correspondente e o não correspondente separadamente, porque geralmente é um caso em que a maioria das linhas corresponde ou a maioria das linhas não.Não é regex, mas achei lógico e útil usar greps seriais com pipe para eliminar o ruído.
por exemplo. procure um arquivo de configuração do apache sem todos os comentários-
e
A lógica do grep serial é (não é um comentário) e (corresponde ao diretório)
fonte
grep -v
good_stuff #comment_stuff
com isso, você evita testar um lookahead em cada posição:
equivalente a (para .net):
Resposta antiga:
fonte
/^[^h]*(?:h+(?!ede)[^h]*)*$/
O mencionado acima
(?:(?!hede).)*
é ótimo porque pode ser ancorado.Mas o seguinte seria suficiente neste caso:
Essa simplificação está pronta para a adição de cláusulas "AND":
fonte
Aqui está como eu faria isso:
Preciso e mais eficiente que as outras respostas. Ele implementa a técnica de eficiência "desenrolar o ciclo" de Friedl e requer muito menos retorno.
fonte
Se você deseja combinar um caractere para negar uma palavra semelhante à classe de caracteres negados:
Por exemplo, uma sequência:
Não use:
Usar:
Aviso
"(?!bbb)."
não é lookbehind nem lookahead, é lookcurrent, por exemplo:fonte
(?!
). O prefixo de lookahead positivo seria(?=
enquanto os prefixos lookbehind correspondentes seriam(?<!
e(?<=
respectivamente. Um lookahead significa que você lê os próximos caracteres (daí “adiante”) sem consumi-los. Um lookbehind significa que você verifica os caracteres que já foram consumidos.Na minha opinião, uma variante mais legível da resposta principal:
Basicamente, "corresponda no início da linha se, e somente se, não tiver 'hede' nela" - portanto, o requisito foi traduzido quase diretamente em regex.
Obviamente, é possível ter vários requisitos de falha:
Detalhes: a âncora ^ garante que o mecanismo regex não tente novamente a correspondência em todos os locais da cadeia, o que corresponderia a todas as cadeias.
A âncora ^ no começo deve representar o início da linha. A ferramenta grep corresponde a cada linha, uma de cada vez, em contextos em que você está trabalhando com uma sequência de múltiplas linhas, você pode usar o sinalizador "m":
ou
fonte
O OP não especificou ou Tag a postagem para indicar o contexto (linguagem de programação, editor, ferramenta) no qual o Regex será usado.
Para mim, às vezes eu preciso fazer isso enquanto edito um arquivo usando
Textpad
.Textpad
suporta alguns Regex, mas não suporta lookahead ou lookbehind, portanto, são necessárias algumas etapas.Se eu estou procurando reter todas as linhas que NÃO contêm a string
hede
, eu faria assim:Agora você tem o texto original com todas as linhas que contêm a string
hede
removida.Se eu estiver olhando para fazer alguma outra coisa, apenas para as linhas que NÃO contêm a sequência
hede
, eu faria assim:fonte
Uma vez que ninguém deu uma resposta direta à pergunta que foi feita , eu vou fazê-lo.
A resposta é que, com o POSIX
grep
, é impossível literalmente atender a essa solicitação:O motivo é que o POSIX
grep
é necessário apenas para trabalhar com expressões regulares básicas , que simplesmente não são poderosas o suficiente para realizar essa tarefa (elas não são capazes de analisar idiomas regulares, devido à falta de alternância e parênteses).No entanto, o GNU
grep
implementa extensões que permitem isso. Em particular,\|
é o operador de alternância na implementação de BREs pelo GNU\(
e\)
são os parênteses. Se seu mecanismo de expressão regular suportar alternância, expressões entre colchetes negativos, parênteses e a estrela Kleene, e conseguir ancorar no início e no final da string, é tudo o que você precisa para essa abordagem. Observe, no entanto, que conjuntos negativos[^ ... ]
são muito convenientes além desses, porque, caso contrário, é necessário substituí-los por uma expressão do formulário(a|b|c| ... )
que lista todos os caracteres que não estão no conjunto, o que é extremamente tedioso e excessivamente longo, ainda mais se todo o conjunto de caracteres é Unicode.Com o GNU
grep
, a resposta seria algo como:(encontrado com o Graal e algumas otimizações adicionais feitas à mão).
Você também pode usar uma ferramenta que implementa expressões regulares estendidas , como
egrep
, para se livrar das barras invertidas:Aqui está um script para testá-lo (observe que ele gera um arquivo
testinput.txt
no diretório atual):No meu sistema, ele imprime:
como esperado.
Para os interessados nos detalhes, a técnica empregada é converter a expressão regular que corresponde à palavra em um autômato finito; uma expressão regular.
Finalmente, como todos observaram, se o seu mecanismo de expressão regular oferecer suporte negativo, isso simplifica bastante a tarefa. Por exemplo, com o GNU grep:
Atualização: Encontrei recentemente a excelente biblioteca FormalTheory de Kendall Hopkins , escrita em PHP, que fornece uma funcionalidade semelhante ao Grail. Usando-o e um simplificador escrito por mim mesmo, eu consegui escrever um gerador on-line de expressões regulares negativas, com uma frase de entrada (apenas caracteres alfanuméricos e de espaço atualmente suportados): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /
Para
hede
isso produz:que é equivalente ao acima.
fonte
Desde a introdução do ruby-2.4.1, podemos usar o novo Operador ausente nas expressões regulares do Ruby
do documento oficial
Assim, no seu caso,
^(?~hede)$
faz o trabalho para vocêfonte
Através do verbo PCRE
(*SKIP)(*F)
Isso pula completamente a linha que contém a string exata
hede
e corresponde a todas as linhas restantes.DEMO
Execução das peças:
Vamos considerar o regex acima, dividindo-o em duas partes.
Parte antes do
|
símbolo. Parte não deve ser correspondida .Parte após o
|
símbolo. Peça deve ser combinada .PARTE 1
O mecanismo Regex iniciará sua execução a partir da primeira parte.
Explicação:
^
Afirma que estamos no início.hede
Corresponde à stringhede
$
Afirma que estamos no final da linha.Portanto, a linha que contém a string
hede
seria correspondida. Depois que o mecanismo regex vê o seguinte verbo(*SKIP)(*F)
( Nota: você pode escrever(*F)
como(*FAIL)
), ele ignora e faz com que a correspondência falhe.|
chamado alteração ou operador OR lógico adicionado ao lado do verbo PCRE, que corresponde a todos os limites existentes entre cada caractere em todas as linhas, exceto a linha que contém a sequência exatahede
. Veja a demonstração aqui . Ou seja, ele tenta corresponder os caracteres da sequência restante. Agora o regex na segunda parte seria executado.PARTE 2
Explicação:
^
Afirma que estamos no início. isto é, corresponde a todas as linhas iniciadas, exceto a dahede
linha. Veja a demonstração aqui ..*
No modo Multilinha,.
corresponderia a qualquer caractere, exceto caracteres de nova linha ou retorno de carro. E*
repetiria o caractere anterior zero ou mais vezes. Então,.*
seria igual a toda a linha. Veja a demonstração aqui .Ei, por que você adicionou. * Em vez de. +?
Porque
.*
corresponderia a uma linha em branco, mas.+
não corresponderá a um espaço em branco. Queremos combinar todas as linhashede
, exceto , pode haver uma possibilidade de linhas em branco também na entrada. então você deve usar em.*
vez de.+
..+
repetiria o caractere anterior uma ou mais vezes. Veja.*
corresponde a uma linha em branco aqui .$
O final da âncora da linha não é necessário aqui.fonte
Pode ser mais sustentável para duas regexes no seu código, uma para executar a primeira correspondência e, se corresponder, execute a segunda regex para verificar casos extremos que você deseja bloquear, por exemplo
^.*(hede).*
, para ter uma lógica apropriada no código.OK, admito que essa não é realmente uma resposta para a pergunta postada e também pode usar um pouco mais de processamento do que uma única regex. Mas para os desenvolvedores que vieram aqui procurando uma solução rápida de emergência para um caso externo, essa solução não deve ser negligenciada.
fonte
Outra opção é que, para adicionar uma visão positiva e verificar se
hehe
há algum lugar na linha de entrada, negaríamos isso, com uma expressão semelhante a:com limites de palavras.
A expressão é explicada no painel superior direito de regex101.com , se você deseja explorar / simplificar / modificá-la e, neste link , é possível ver como ela corresponderia a algumas entradas de amostra, se desejar.
Circuito RegEx
O jex.im visualiza expressões regulares:
fonte
A linguagem TXR suporta negação de regex.
Um exemplo mais complicado: combine todas as linhas que começam
a
e terminam comz
, mas não contêm a substringhede
:A negação de Regex não é particularmente útil por si só, mas quando você também tem interseção, as coisas ficam interessantes, pois você tem um conjunto completo de operações de conjuntos booleanos: você pode expressar "o conjunto que corresponde a isso, exceto as que correspondem a esse".
fonte
A função abaixo ajudará você a obter a saída desejada
fonte
^ ((?! hede).) * $ é uma solução elegante, exceto porque consome caracteres, você não poderá combiná-lo com outros critérios. Por exemplo, digamos que você queira verificar a não presença de "hede" e a presença de "haha". Esta solução funcionaria porque não consumirá caracteres:
^ (?!. \ bhede \ b) (? =. \ bhaha \ b)
fonte
Como usar os verbos de controle de retrocesso do PCRE para corresponder a uma linha que não contém uma palavra
Aqui está um método que eu nunca vi usado antes:
Como funciona
Primeiro, ele tenta encontrar "hede" em algum lugar da linha. Se for bem-sucedido, nesse ponto,
(*COMMIT)
instrui o mecanismo a, não apenas voltar atrás em caso de falha, mas também não tentar nenhuma correspondência adicional nesse caso. Em seguida, tentamos corresponder a algo que não pode ser correspondido (neste caso^
).Se uma linha não contiver "hede", a segunda alternativa, um subpadrão vazio, corresponderá com êxito à string do assunto.
Esse método não é mais eficiente do que um visual negativo, mas eu pensei em usá-lo aqui para o caso de alguém o achar bacana e utilizá-lo para outras aplicações mais interessantes.
fonte
Uma solução mais simples é usar o operador not !
Sua instrução if precisará corresponder a "contém" e não a "exclui".
Acredito que os designers da RegEx anteciparam o uso de não operadores.
fonte
Talvez você encontre isso no Google enquanto tenta escrever uma regex capaz de corresponder aos segmentos de uma linha (em oposição a linhas inteiras) que não contêm substring. Demorei um pouco para descobrir, então vou compartilhar:
Dada uma sequência:
<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>
Quero corresponder
<span>
tags que não contêm a substring "ruim"./<span(?:(?!bad).)*?>
irá combinar<span class=\"good\">
e<span class=\"ugly\">
.Observe que existem dois conjuntos (camadas) de parênteses:
Demonstração em Ruby:
fonte
Com o ConyEdit , você pode usar a linha de comando
cc.gl !/hede/
para obter linhas que não contêm a correspondência de regex ou usar a linha de comandocc.dl /hede/
para excluir linhas que contêm a correspondência de regex. Eles têm o mesmo resultado.fonte
Eu queria adicionar outro exemplo, se você estiver tentando corresponder uma linha inteira que contenha a cadeia X , mas também não contenha a cadeia Y .
Por exemplo, digamos que queremos verificar se nosso URL / string contém " guloseimas saborosas ", desde que também não contenha " chocolate " em nenhum lugar.
Esse padrão de regex funcionaria (também funciona em JavaScript)
(sinalizadores globais de várias linhas no exemplo)
Exemplo interativo: https://regexr.com/53gv4
Fósforos
(Esses URLs contêm "guloseimas saborosas" e também não contêm "chocolate")
Não corresponde
(Esses URLs contêm "chocolate" em algum lugar - para que não correspondam, mesmo que contenham "guloseimas saborosas")
fonte