Um regex para corresponder a uma substring que não é seguida por uma determinada outra substring

116

Preciso de uma regex que corresponda, blahfooblahmas nãoblahfoobarblah

Quero que corresponda apenas a foo e tudo ao redor de foo, contanto que não seja seguido por bar.

Tentei usar isto: foo.*(?<!bar)que é bastante parecido, mas corresponde blahfoobarblah. O olhar negativo por trás precisa corresponder a qualquer coisa e não apenas à barra.

A linguagem específica que estou usando é o Clojure, que usa regexes Java nos bastidores.

EDIT: Mais especificamente, eu também preciso passar, blahfooblahfoobarblahmas não blahfoobarblahblah.

Rayne
fonte
1
Você tentou usar foo. * (? <! Bar. *)?
Thibault Falise

Respostas:

158

Experimentar:

/(?!.*bar)(?=.*foo)^(\w+)$/

Testes:

blahfooblah            # pass
blahfooblahbarfail     # fail
somethingfoo           # pass
shouldbarfooshouldfail # fail
barfoofail             # fail

Explicação da expressão regular

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                             (matching the most amount possible))
--------------------------------------------------------------------------------
    bar                      'bar'
--------------------------------------------------------------------------------
  )                        end of look-ahead
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                             (matching the most amount possible))
--------------------------------------------------------------------------------
    foo                      'foo'
--------------------------------------------------------------------------------
  )                        end of look-ahead
--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    \w+                      word characters (a-z, A-Z, 0-9, _) (1 or
                             more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string

Outro regex

Se você deseja excluir apenas barquando for logo após foo, você pode usar

/(?!.*foobar)(?=.*foo)^(\w+)$/

Editar

Você atualizou sua pergunta para torná-la específica.

/(?=.*foo(?!bar))^(\w+)$/

Novos testes

fooshouldbarpass               # pass
butnotfoobarfail               # fail
fooshouldpassevenwithfoobar    # pass
nofuuhere                      # fail

Nova explicação

(?=.*foo(?!bar))garante que um fooseja encontrado, mas não seja seguido diretamentebar

Macek
fonte
Esta é uma resposta muito próxima e muito boa. Eu sabia que não seria específico o suficiente. :( Eu preciso disso: "blahfoomeowwoof / foobar /" para passar por causa do solitário "foo", mas não desse blahfoobarmeowwoof Se isso for possível.
Rayne
Como uma pergunta lateral, como alguém faria para combinar algo como "bot", mas não "botters"?
Rayne
Sim. Posso usar o que tenho agora, mas seria mais fácil se eu pudesse apenas combinar bot, mas não botters. Eu sinto muito. Sou inexperiente com regexes e tenho medo de estar lentamente descobrindo o que quero sozinho. : p
Rayne
1
@Rayne, essa é a mesma pergunta. Em seu exemplo acima, você queria corresponder, foomas não foobar. Para combinar, botmas não botters, você usaria /(?=.*bot(?!ters))^(\w+)$/.
maček
Bem, geralmente eu estava visando palavras inteiras. Como eu disse, estou confuso sobre o que realmente quero e o que é realmente possível. Fazer assim vai funcionar. Obrigado pelo tempo. :)
Rayne
55

Para corresponder a um fooseguimento por algo que não começa com bar, tente

foo(?!bar)

Sua versão com lookbehind negativo é efetivamente "corresponde a um fooseguido por algo que não termina em bar". O .*corresponde totalmente barblah, e (?<!bar)olha para trás lahe verifica se não corresponde bar, o que não é verdade, de modo que todo o padrão coincide.

Stevemegson
fonte
Tentei fazer isso para uma regex projetada para corresponder à string "did you", desde que não seja seguida por "say". Funciona ao diferenciar entre "você disse" e "você achou", por exemplo, mas apenas "você" por si só não é capturado, e deveria. Alguma sugestão?
soosus
2

Em vez disso, use uma previsão negativa:

\s*(?!\w*(bar)\w*)\w*(foo)\w*\s*

Isso funcionou para mim, espero que ajude. Boa sorte!

Audie
fonte
Regex simples, mas eficaz, que também funciona para excluir sequências de repetição ("foofoo"). Perfeito!
Jonas Byström
1

Você escreveu um comentário sugerindo que isso funcione com a correspondência de todas as palavras em uma string em vez da própria string inteira.

Em vez de misturar tudo isso em um comentário, estou postando como uma nova resposta.

Novo Regex

/(?=\w*foo(?!bar))(\w+)/

Texto de amostra

foowithbar fooevenwithfoobar notfoobar foohere notfoobarhere butfooisokherebar notfoobarhere andnofuu needsfoo

Partidas

foowithbar fooevenwithfoobar foohere butfooisokherebar needfoo

Macek
fonte
0

Sua solicitação de correspondência específica pode ser correspondida por:

\w+foo(?!bar)\w+

Isso vai combinar, blahfooblahfoobarblahmas não blahfoobarblahblah.

O problema com sua regex de foo.*(?<!bar)é o .*depois foo. Corresponde a tantos caracteres, incluindo caracteres posteriores bar.

dawg
fonte