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 smodificador 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 cowcom 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, milkverificados (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 milkdeve seguir (e aqueles são combinados e consumidos , também )
Cenário 2: Entrada multilinha
cow ([\s\S]*?) milk
Aqui, cowe 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+ whitespacee >>>, não pode usá-la, />>>\d+\s(.*?)>>>/gpois isso encontrará apenas 1 correspondência, porque o >>>antes 67já 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 text1e text2como 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 cowe milkde "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]*?)\nmilkpodermos 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#matchAllmé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]));