javascript regex - olhar para trás alternativa?

143

Aqui está uma regex que funciona bem na maioria das implementações de regex:

(?<!filename)\.js$

Corresponde a .js para uma sequência que termina com .js, exceto para filename.js

Javascript não tem regex lookbehind. Alguém pode montar um regex alternativo que atinja o mesmo resultado e funciona em javascript?

Aqui estão alguns pensamentos, mas precisa de funções auxiliares. Eu esperava conseguir isso apenas com um regex: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript

daniel
fonte
3
se você só precisa verificar um nome de arquivo ou lista de nomes de arquivos específicos, por que não usar apenas duas verificações? verifique se termina em .js e, em caso afirmativo, verifique se não corresponde a filename.js ou vice-versa.
si28719e
3
Atualização: a versão pública mais recente do Chrome (v62) inclui lookbehinds (presumivelmente experimentais) prontos para uso: D Observe, no entanto, que os lookbehinds ainda estão no estágio 3 da proposta: github.com/tc39/proposal-regexp-lookbehind . Portanto, pode demorar um pouco até que o JavaScript o suporte em todos os lugares. É melhor ter cuidado ao usar na produção!
Eirik Birkeland
2
# Update: ES2018 inclui afirmações lookbehind Mais de : - Modo dotall (o sinalizador s) - afirmações lookbehind - grupos de captura nomeados - Unicode escapes de propriedade
Ashley Coolman
2
Basta usar (?<=thingy)thingypara olhar(?<!thingy)thingy para trás e para negativo . Agora ele os suporta.
Константин Ван
7
@ K._ A partir de fevereiro de 2018, isso ainda não é verdade !! E isso levará algum tempo, porque navegadores e mecanismos devem implementar a especificação (atual no rascunho).
Andre Figueiredo

Respostas:

64

^(?!filename).+\.js funciona para mim

testado contra:

  • correspondência test.js
  • correspondência de blabla.js
  • filename.js não corresponde

Uma explicação adequada para esse regex pode ser encontrada em Expressão regular para corresponder à string que não contém uma palavra?

O Look Forward está disponível desde a versão 1.5 do javascript e é suportado por todos os principais navegadores

Atualizado para corresponder a filename2.js e 2filename.js, mas não filename.js

(^(?!filename\.js$).).+\.js

Benjamin Udink ten Cate
fonte
5
Essa pergunta que você vinculou às conversas sobre um problema ligeiramente diferente: corresponder a uma sequência que não contém a palavra de destino em lugar algum . Essa é muito mais simples: combinar uma string que não começa com a palavra de destino.
Alan Moore
Isso é muito bom, apenas perde casos como: filename2.js ou filenameddk.js ou similar. Esta não é uma correspondência, mas deve ser uma correspondência.
Daniel
9
@daniel Você pediu um olhar para trás, não um olhar para o futuro, por que você aceitou esta resposta?
Hek2mgl
1
o dado não correspondea.js
inetphantom 17/03
1
A regex original com lookbehind não corresponde 2filename.js, mas a regex fornecida aqui corresponde. Um mais apropriado seria ^(?!.*filename\.js$).*\.js$. Isso significa, corresponda a qualquer um, *.js exceto *filename.js .
weibeld 16/05
153

EDIT: A partir do ECMAScript 2018 em diante, as assertivas lookbehind (mesmo ilimitadas) são suportadas nativamente .

Nas versões anteriores, você pode fazer isso:

^(?:(?!filename\.js$).)*\.js$

Isso faz explicitamente o que a expressão lookbehind está fazendo implicitamente: verifique cada caractere da sequência se a expressão lookbehind mais o regex depois que ela não corresponderá e só permita que esse caractere corresponda.

^                 # Start of string
(?:               # Try to match the following:
 (?!              # First assert that we can't match the following:
  filename\.js    # filename.js 
  $               # and end-of-string
 )                # End of negative lookahead
 .                # Match any character
)*                # Repeat as needed
\.js              # Match .js
$                 # End of string

Outra edição:

Dói-me dizer (especialmente porque essa resposta foi tão votada) que existe uma maneira muito mais fácil de atingir esse objetivo. Não há necessidade de verificar a aparência de cada personagem:

^(?!.*filename\.js$).*\.js$

funciona tão bem:

^                 # Start of string
(?!               # Assert that we can't match the following:
 .*               # any string, 
  filename\.js    # followed by filename.js
  $               # and end-of-string
)                 # End of negative lookahead
.*                # Match any string
\.js              # Match .js
$                 # End of string
Tim Pietzcker
fonte
Funciona em muitos casos, exceto onde há caracteres anteriores, por exemplo: filename.js (nomatch de obras) filename2.js (correspondência de trabalhos) blah.js (funciona - correspondência) 2filename.js (não funciona - nomatch) --- tendo dito isso, o lookbehind tem a mesma limitação que eu não percebi até agora ...
daniel
9
@daniel: Bem, seu regex (com lookbehind) também não corresponde 2filename.js. Minha regex corresponde exatamente aos mesmos casos que a regex de exemplo.
Tim Pietzcker
Perdoe minha ingenuidade, mas há alguma utilidade para o grupo que não captura aqui? Eu sempre soube que isso só é útil ao tentar reunir referências para substituição em uma string. Até onde eu sei, isso também funcionará ^ (?! nome do arquivo \ .js $). * \. Js $
Eu quero respostas
1
Não é bem assim, que o regex verifica "filename.js" apenas no início da string. Mas ^(?!.*filename\.js$).*\.js$funcionaria. Tentando pensar em situações em que o ncgroup ainda pode ser necessário ...
Tim Pietzcker
Essa abordagem pode ser resumida como: em vez de olhar para trás do X, observe todos os personagens que vêm antes do X?
Salsaparrilha
25

Vamos supor que você queira encontrar tudo que intnão é precedido por unsigned:

Com suporte para look-behind negativo:

(?<!unsigned )int

Sem suporte para look-behind negativo:

((?!unsigned ).{9}|^.{0,8})int

Basicamente, a idéia é pegar n caracteres anteriores e excluir a correspondência com uma previsão negativa, mas também corresponder aos casos em que não há n caracteres anteriores. (onde n é o comprimento do look-behind).

Então, o regex em questão:

(?<!filename)\.js$

traduziria para:

((?!filename).{8}|^.{0,7})\.js$

Pode ser necessário brincar com a captura de grupos para encontrar o ponto exato da sequência que lhe interessa ou você não deseja substituir uma parte específica por outra.

Kamil Szot
fonte
Acabei de converter isso: (?<!barna)(?<!ene)(?<!en)(?<!erne) (?:sin|vår)e?(?:$| (?!egen|egne))para (?!barna).(?!erne).(?!ene).(?!en).. (?:sin|vår)e?(?:$| (?!egen|egne))qual faz o truque para minhas necessidades. Apenas fornecendo isso como outro cenário do "mundo real". Ver link
Eirik Birkeland
Eu acho que você quis dizer:((?!unsigned ).{9}|^.{0,8})int
pansay
@pansay Sim. Obrigado. Acabei de corrigir minha resposta.
21417 Kamil Szot
2
Obrigado pela resposta mais generalizada, que funciona mesmo onde há necessidade de corresponder profundamente ao texto (onde ^ inicial seria impraticável)!
Milos Mrdovic 17/08/19
5

Se você puder olhar para frente, mas para trás, poderá inverter a corda primeiro e, em seguida, dar uma olhada. Um pouco mais de trabalho precisará ser feito, é claro.

Albert Friend
fonte
8
Esta resposta poderia realmente usar alguma melhoria. Parece mais um comentário para mim.
Mckmackusa # 26/18
2

Esta é uma solução equivalente à resposta de Tim Pietzcker (veja também comentários da mesma resposta):

^(?!.*filename\.js$).*\.js$

Significa, combinar *.js exceto *filename.js.

Para chegar a essa solução, você pode verificar quais padrões o negativo por trás exclui e depois excluir exatamente esses padrões com um negativo negativo.

Weibeld
fonte
-1

Abaixo, há uma alternativa positiva ao JavaScript, mostrando como capturar o sobrenome das pessoas com 'Michael' como primeiro nome.

1) Dado este texto:

const exampleText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";

obter uma matriz de sobrenomes de pessoas chamadas Michael. O resultado deve ser:["Jordan","Johnson","Green","Wood"]

2) Solução:

function getMichaelLastName2(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(person.indexOf(' ')+1));
}

// or even
    .map(person => person.slice(8)); // since we know the length of "Michael "

3) Verifique a solução

console.log(JSON.stringify(    getMichaelLastName(exampleText)    ));
// ["Jordan","Johnson","Green","Wood"]

Demonstração aqui: http://codepen.io/PiotrBerebecki/pen/GjwRoo

Você também pode experimentá-lo executando o snippet abaixo.

const inputText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";



function getMichaelLastName(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(8));
}

console.log(JSON.stringify(    getMichaelLastName(inputText)    ));

Piotr Berebecki
fonte