Expressão regular para obter uma string entre duas strings em JavaScript
A solução mais completa que funcionará na grande maioria dos casos é usar um grupo de captura com um padrão de correspondência de pontos preguiçosos . No entanto, um ponto .
em JavaScript regex não coincide com caracteres de quebra de linha, por isso, o que vai funcionar em 100% dos casos é um [^]
ou [\s\S]
/ [\d\D]
/ [\w\W]
construções.
ECMAScript 2018 e mais recente solução compatível
Nos ambientes JavaScript que suportam o ECMAScript 2018 , o s
modificador permite .
corresponder qualquer caractere, incluindo caracteres de quebra de linha, e o mecanismo regex suporta lookbehinds de comprimento variável. Então, você pode usar um regex como
var result = s.match(/(?<=cow\s+).*?(?=\s+milk)/gs); // Returns multiple matches if any
// Or
var result = s.match(/(?<=cow\s*).*?(?=\s*milk)/gs); // Same but whitespaces are optional
Em ambos os casos, a posição atual é verificada cow
com 1/0 ou mais espaços em branco depois e cow
, em seguida, quaisquer 0 ou mais caracteres possíveis são correspondidos e consumidos (= adicionados ao valor da correspondência) e, em seguida, milk
verificados (com qualquer 1/0 ou mais espaços em branco antes dessa substring).
Cenário 1: Entrada de linha única
Este e todos os outros cenários abaixo são suportados por todos os ambientes JavaScript. Veja exemplos de uso na parte inferior da resposta.
cow (.*?) milk
cow
é encontrado em primeiro lugar, em seguida, um espaço, em seguida, quaisquer outros do que caracteres de quebra de linha 0+ caracteres, o menor número possível como *?
é um quantificador preguiçoso, são capturados em Grupo 1 e, em seguida, um espaço com milk
deve seguir (e aqueles são combinados e consumidos , também )
Cenário 2: Entrada multilinha
cow ([\s\S]*?) milk
Aqui, cow
e um espaço é correspondido primeiro, em seguida, quaisquer 0 + caracteres o menor possível são correspondidos e capturados no Grupo 1 e, em seguida, um espaço milk
é correspondido.
Cenário 3: correspondências sobrepostas
Se você tem uma string como essa >>>15 text>>>67 text2>>>
e precisa obter duas correspondências entre >>>
+ number
+ whitespace
e >>>
, não pode usá-la, />>>\d+\s(.*?)>>>/g
pois isso encontrará apenas 1 correspondência, porque o >>>
antes 67
já foi consumido ao encontrar a primeira correspondência. Você pode usar um lookahead positivo para verificar a presença do texto sem "devorá-lo" (por exemplo, acrescentando à correspondência):
/>>>\d+\s(.*?)(?=>>>)/g
Veja a regex demonstração online rendendo text1
e text2
como Grupo 1 conteúdos encontrados.
Consulte também Como obter todas as correspondências sobrepostas possíveis para uma string .
Considerações de desempenho
O padrão de correspondência de pontos preguiçosos ( .*?
) dentro dos padrões regex pode retardar a execução do script se for fornecida uma entrada muito longa. Em muitos casos, a técnica de desenrolar o loop ajuda em maior extensão. Tentando pegar tudo entre cow
e milk
de "Their\ncow\ngives\nmore\nmilk"
, vemos que precisamos apenas corresponder a todas as linhas que não começam milk
, portanto, em vez de cow\n([\s\S]*?)\nmilk
podermos usar:
/cow\n(.*(?:\n(?!milk$).*)*)\nmilk/gm
Veja a demonstração do regex (se possível \r\n
, use /cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm
). Com essa pequena sequência de teste, o ganho de desempenho é insignificante, mas com um texto muito grande, você sentirá a diferença (especialmente se as linhas forem longas e as quebras de linha não forem muito numerosas).
Exemplo de uso de regex em JavaScript:
//Single/First match expected: use no global modifier and access match[1]
console.log("My cow always gives milk".match(/cow (.*?) milk/)[1]);
// Multiple matches: get multiple matches with a global modifier and
// trim the results if length of leading/trailing delimiters is known
var s = "My cow always gives milk, thier cow also gives milk";
console.log(s.match(/cow (.*?) milk/g).map(function(x) {return x.substr(4,x.length-9);}));
//or use RegExp#exec inside a loop to collect all the Group 1 contents
var result = [], m, rx = /cow (.*?) milk/g;
while ((m=rx.exec(s)) !== null) {
result.push(m[1]);
}
console.log(result);
Usando o String#matchAll
método moderno
const s = "My cow always gives milk, thier cow also gives milk";
const matches = s.matchAll(/cow (.*?) milk/g);
console.log(Array.from(matches, x => x[1]));