Como fazer “correspondência inversa” com regex?

112

Estou usando o RegexBuddy, mas estou com problemas mesmo assim: \

Estou processando um arquivo linha por linha. Construí um "modelo de linha" para combinar com o que desejo.

Agora eu gostaria de fazer uma correspondência inversa ... ou seja, eu quero combinar as linhas onde há uma sequência de 6 letras, mas apenas se essas seis letras não forem Andrea , como devo fazer isso?


EDITAR: Vou escrever o programa que usa este regex, não sei ainda se em python ou php, estou fazendo isso primeiro para aprender um pouco de regex :) Existem diferentes tipos de linha, eu queria usar regex para selecionar o tipo no qual estou interessado. Depois de obter essas linhas, tenho que aplicar outro filtro apenas para não corresponder a um valor conhecido, preciso de todos os outros, não disso. O (?! Não desejado) está funcionando muito bem, obrigado. :-)

Espero que isso esclareça a questão :)

Andrea Ambu
fonte
Na verdade, parece que você faria melhor se nos desse um pouco mais de informações sobre o que está fazendo e ver se alguém pode oferecer uma solução alternativa. Normalmente, tentar analisar um arquivo inteiro construindo uma expressão regular que corresponda a cada linha é um caminho bastante complicado :)
Dan,

Respostas:

70
(?!Andrea).{6}

Supondo que seu mecanismo regexp suporte lookaheads negativos.

Edit: ..ou talvez você prefira usar [A-Za-z]{6}no lugar de.{6}

Editar (novamente): Observe que lookaheads e lookbehinds geralmente não são a maneira certa de "inverter" uma correspondência de expressão regular. Regexps não são realmente configurados para fazer correspondência negativa, eles deixam isso para qualquer idioma que você está usando.

Dan
fonte
Você precisa adicionar o ^ que @Vinko Vrsalovic usa para que não corresponda em "ndrea \ n"
bdukes,
2
. não combina \ n por padrão (alguns idiomas [por exemplo, Perl] permitem que você ative esse comportamento, mas por padrão. combina com tudo, MAS \ n).
Dan,
1
(além disso, o OP nunca mencionou que a sequência precisava ocorrer no início da linha)
Dan,
1
o que você quer dizer com OP?
Andrea Ambu,
1
Andrea: OP significa "pôster original", então, eu estava me referindo a você :)
Dan,
47

Para Python / Java,

^(.(?!(some text)))*$

http://www.lisnichenko.com/articles/javapython-inverse-regex.html

Dmytro
fonte
4
Isso não funciona. Você está pensando no idioma Tempered Greedy Token. mas o ponto precisa ir após a antevisão, não antes. Veja esta pergunta . Mas essa abordagem é um exagero para essa tarefa de qualquer maneira.
Alan Moore
Não sei em qual idioma está escrito, mas funcionou perfeitamente no Sublime text para limpar meus dados de teste. Obrigado!
Matthias dirickx
1
@AlanMoore Na verdade, quase funcionará neste caso de uso. No entanto, se some textiniciar a linha, ele retornará o resultado errado.
Zenexer
2
@Zenexer, foi isso que eu quis dizer. Se o ponto estiver depois da antevisão em vez de antes, funcionará perfeitamente.
Alan Moore de
Aqui está um link que explica mais. Não entendo por que ?!e não apenas !.
Timo
21

Atualizado com feedback de Alan Moore

Em PCRE e variantes semelhantes, você pode criar uma regex que corresponda a qualquer linha que não contenha um valor:

^(?:(?!Andrea).)*$

Isso é chamado de token ganancioso moderado . A desvantagem é que ele não funciona bem.

Zenexer
fonte
1
Este é o Símbolo Ganancioso Temperado na forma longa. Basta colocar o ponto (ou [\s\S], o que só é útil em JavaScript) após o segundo lookahead, e você não precisa do primeiro: ^(?:(?!Andrea).)*$.
Alan Moore
@AlanMoore Nice! Não consegui encontrar nenhum padrão estabelecido que funcionasse assim, então criei o meu próprio. Em vez de eu aceitar sua resposta, você deve fornecê-la como sua.
Zenexer
Tudo bem, já existem muitas respostas boas. E você merece crédito por inventar o idioma sozinho. Felicidades!
Alan Moore
Por que você sugere usar [\S\s]? OP está falando sobre linhas de correspondência, não contendo a palavra "Andrea". Não sobre verificar se a string inteira contém essa palavra. Estou esquecendo de algo?
x-yuri
@ x-yuri Acho que você está certo. Provavelmente respondi à minha pergunta quando visitei esta página pela primeira vez, ignorando a discrepância. Minha conexão não é boa o suficiente para atualizar a resposta agora, no entanto (<10 kbps)
Zenexer
11

Que linguagem você está usando? Os recursos e a sintaxe da implementação de regex são importantes para isso.

Você pode usar a antecipação. Usando python como exemplo

import re

not_andrea = re.compile('(?!Andrea)\w{6}', re.IGNORECASE)

Para quebrar isso:

(?! Andrea) significa 'combinar se os próximos 6 caracteres não forem "Andrea"'; se sim então

\ w significa um "caractere de palavra" - caracteres alfanuméricos. Isso é equivalente à classe [a-zA-Z0-9_]

\ w {6} significa exatamente 6 caracteres de palavras.

re.IGNORECASE significa que você excluirá "Andrea", "andrea", "ANDREA" ...

Outra maneira é usar a lógica do programa - usar todas as linhas que não correspondem a Andrea e colocá-las em uma segunda regex para verificar se há 6 caracteres. Ou primeiro verifique se há pelo menos 6 caracteres de palavra e depois verifique se não corresponde a Andrea.

Hamish Downer
fonte
7

Asserção antecipada negativa

(?!Andrea)

Esta não é exatamente uma correspondência invertida, mas é o melhor que você pode fazer diretamente com regex. Porém, nem todas as plataformas os suportam.

Vinko Vrsalovic
fonte
1
Até que o questionador esclareça, não vejo que a partida tenha que começar no início da linha. Então, por que o ^?
Hamish Downer,
Porque eu entendi que ele queria checar no início da linha, editada dada esclarecimentos
Vinko Vrsalovic
5

Se você quiser fazer isso no RegexBuddy, há duas maneiras de obter uma lista de todas as linhas que não correspondem a um regex.

Na barra de ferramentas do painel Teste, defina o escopo do teste como "Linha por linha". Ao fazer isso, um item Listar todas as linhas sem correspondências aparecerá no botão Listar tudo na mesma barra de ferramentas. (Se você não vir o botão Listar Tudo, clique no botão Corresponder na barra de ferramentas principal.)

No painel GREP, você pode ativar as caixas de seleção "baseado em linha" e "inverter resultados" para obter uma lista de linhas não correspondentes nos arquivos que você está pesquisando.

Jan Goyvaerts
fonte
5

(?!é útil na prática. Embora estritamente falando, olhar para a frente não é uma expressão regular definida matematicamente.

Você pode escrever uma expressão regular invertida manualmente.

Aqui está um programa para calcular o resultado automaticamente. Seu resultado é gerado por máquina, o que geralmente é muito mais complexo do que escrever à mão. Mas o resultado funciona.

fraco
fonte
1

Acabei de criar este método que pode exigir muito do hardware, mas está funcionando:

Você pode substituir todos os caracteres que correspondem ao regex por uma string vazia.

Este é um oneliner:

notMatched = re.sub(regex, "", string)

Usei isso porque fui forçado a usar uma regex muito complexa e não consegui descobrir como inverter todas as suas partes em um período de tempo razoável.

Isso retornará apenas o resultado da string, não quaisquer objetos correspondentes!

Matthias Herrmann
fonte
-3

Em perl você pode fazer

processo ($ linha) if ($ linha = ~! / Andrea /);

Phreakre
fonte
4
Essa sintaxe está errada. Acho que você quer dizer processo ($ linha) se $ linha! ~ / Andrea /
dland