Espressione regolare per ottenere una stringa tra due stringhe in Javascript


166

Ho trovato post molto simili, ma non riesco proprio a trovare la mia espressione regolare proprio qui.

Sto cercando di scrivere un'espressione regolare che restituisce una stringa che si trova tra altre due stringhe. Ad esempio: voglio ottenere la stringa che risiede tra le stringhe "mucca" e "latte".

La mia mucca dà sempre latte

sarebbe tornato

"dà sempre"

Ecco l'espressione che ho messo insieme finora:

(?=cow).*(?=milk)

Tuttavia, questo restituisce la stringa "mucca dà sempre".


6
Mi sono imbattuto in questa vecchia domanda e volevo chiarire perché testRE è un array. test.match restituisce un array con il primo indice come corrispondenza totale (quindi, la stringa che corrisponde al latte di vacca (. *)) e quindi, tutte le stringhe intrappolate come il (. *) se fosse presente una seconda serie di parentesi quindi essere in testRE [2]
Salketer

4
Questa soluzione non funzionerà se stai cercando una stringa contenente newline. In tal caso, è necessario utilizzare "STRING_ONE ([\\ s \\ S] *?) STRING_TWO". stackoverflow.com/questions/22531252/…
Michael.Lumley,

solo per riferimento il metodo di corrispondenza su MDN developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
vzR

Risposte:


183

Un lookahead (quella (?=parte) non consuma alcun input. È un'asserzione di larghezza zero (come lo sono i controlli di confine e i lookbehinds).

Vuoi una partita regolare qui, per consumare la cowporzione. Per catturare la porzione in mezzo, si utilizza un gruppo di acquisizione (basta inserire la parte del motivo che si desidera acquisire tra parentesi):

cow(.*)milk

Non sono necessari lookaheads.


26
Quando provo questo, l'espressione Regex fornita include sia "mucca" che "latte" ...
TheCascadian

4
Questo manca un passo. Quando si ottiene il risultato della corrispondenza, è necessario estrarre il testo corrispondente del primo gruppo di acquisizione matched[1], non l'intero testo corrispondente matched[0].
Rory O'Kane,

7
In Javascript, in realtà è necessario utilizzare ([\s\S]*?)anziché (.*?).
Qian Chen,

7
Sebbene questa sia una tecnica utile, è stata sottoposta a downgrade perché IMHO NON è la risposta giusta alla domanda, poiché include "mucca" e "latte", come affermato da @TheCascadian
Almir Campos il

@AlmirCampos - se non sbaglio non c'è modo di fare questa partita senza abbinare "mucca" e "latte" (dal momento che vuoi abbinare ciò che c'è tra quei due). Il problema non è nel RegEx stesso, ma come lo gestisci in seguito (come menzionato da Rory O'Kane). Altrimenti potresti abbinare solo per gli spazi circostanti - e questo ti darebbe un ritorno MOLTO sbagliato, no?
sborn

69

Espressione regolare per ottenere una stringa tra due stringhe in JavaScript

La soluzione più completa che funzionerà nella stragrande maggioranza dei casi è l'utilizzo di un gruppo di acquisizione con un modello di corrispondenza punti pigro . Tuttavia, un punto .in regex JavaScript non corrisponde ai caratteri di interruzione di riga, quindi ciò che funzionerà nel 100% dei casi è un [^]o [\s\S]/ [\d\D]/ [\w\W]costrutti.

ECMAScript 2018 e soluzione compatibile più recente

Negli ambienti JavaScript che supportano ECMAScript 2018 , il smodificatore consente .di abbinare qualsiasi carattere, inclusi i caratteri di interruzione di riga, e il motore regex supporta lookbehind di lunghezza variabile. Quindi, puoi usare un regex come

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

In entrambi i casi, la posizione corrente viene controllata cowcon 1/0 o più spazi bianchi dopo cow, quindi ogni 0+ caratteri il meno possibile vengono abbinati e consumati (= aggiunti al valore di corrispondenza), quindi milkviene verificato (con qualsiasi 1/0 o più spazi bianchi prima di questa sottostringa).

Scenario 1: input a riga singola

Questo e tutti gli altri scenari di seguito sono supportati da tutti gli ambienti JavaScript. Vedi esempi di utilizzo in fondo alla risposta.

cow (.*?) milk

cowsi trova, poi uno spazio, allora qualsiasi 0+ caratteri diversi da caratteri di interruzione di riga, il meno possibile, come *?è un quantificatore pigrizia, vengono catturati in gruppo 1 e quindi uno spazio con milkdeve seguire (e quelli sono abbinati e consumati , troppo ).

Scenario 2: input multilinea

cow ([\s\S]*?) milk

Qui, cowe uno spazio viene prima abbinato, quindi ogni 0+ caratteri il meno possibile vengono abbinati e catturati nel Gruppo 1, e quindi uno spazio con milkviene abbinato.

Scenario 3: partite sovrapposte

Se hai una stringa simile >>>15 text>>>67 text2>>>e hai bisogno di ottenere 2 corrispondenze tra >>>+ number+ whitespacee >>>, non puoi usarlo />>>\d+\s(.*?)>>>/gpoiché questo troverà solo 1 corrispondenza a causa del fatto che il >>>precedente 67è già consumato quando trova la prima corrispondenza. È possibile utilizzare un lookahead positivo per verificare la presenza del testo senza effettivamente "divorarlo" (ovvero accodando alla corrispondenza):

/>>>\d+\s(.*?)(?=>>>)/g

Guarda la resa in linea di regex onlinetext1 e text2come contenuto del Gruppo 1 trovato.

Vedi anche Come ottenere tutte le possibili corrispondenze sovrapposte per una stringa .

Considerazioni sulle prestazioni

Il pattern di corrispondenza dei punti pigri ( .*?) all'interno dei pattern regex può rallentare l'esecuzione dello script se viene fornito un input molto lungo. In molti casi, la tecnica di svolgimento del ciclo aiuta in misura maggiore. Cercando di afferrare tutto tra cowe milkda "Their\ncow\ngives\nmore\nmilk", vediamo che dobbiamo solo abbinare tutte le linee che non iniziano con milk, quindi, invece di cow\n([\s\S]*?)\nmilkpoter usare:

/cow\n(.*(?:\n(?!milk$).*)*)\nmilk/gm

Guarda la demo di regex (se possibile \r\n, usa /cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm). Con questa piccola stringa di prova, il guadagno in termini di prestazioni è trascurabile, ma con un testo molto grande, sentirai la differenza (specialmente se le linee sono lunghe e le interruzioni di riga non sono molto numerose).

Esempio di utilizzo di regex in 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);

Utilizzando il String#matchAllmetodo 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]));


51

Ecco una regex che afferrerà cosa c'è tra mucca e latte (senza spazio iniziale / finale):

srctext = "My cow always gives milk.";
var re = /(.*cow\s+)(.*)(\s+milk.*)/;
var newtext = srctext.replace(re, "$2");

Un esempio: http://jsfiddle.net/entropo/tkP74/


17
  • Devi catturare il file .*
  • Puoi (ma non devi) fare il non- .*goloso
  • Non c'è davvero bisogno del lookahead.

    > /cow(.*?)milk/i.exec('My cow always gives milk');
    ["cow always gives milk", " always gives "]

In questo caso particolare, se fosse avido raggiungerebbe la fine e il backtrack (presumibilmente).
Ben

9

La risposta scelta non ha funzionato per me ... hmm ...

Basta aggiungere spazio dopo la mucca e / o prima del latte per tagliare gli spazi da "dà sempre"

/(?<=cow ).*(?= milk)/

inserisci qui la descrizione dell'immagine


Non è necessario commentare la tua risposta, modificala.
Cody G

Look Behind ?<=non è supportato in Javascript.
Mark Carpenter Jr,

@MarkCarpenterJr se l'hai testato tramite regextester.com , otterrai quel suggerimento. Sembra che il sito abbia basato le sue regole dalle specifiche precedenti. Lookbehind è ora supportato. Vedi stackoverflow.com/questions/30118815/… E il modello funziona bene con i browser moderni senza errori. Prova questo checker invece regex101.com
duduwe

@ CodyG.ah sì. fatto.
Duduwe,

8

Sono stato in grado di ottenere ciò di cui avevo bisogno usando la soluzione di Martinho Fernandes di seguito. Il codice è:

var test = "My cow always gives milk";

var testRE = test.match("cow(.*)milk");
alert(testRE[1]);

Noterai che sto avvisando la variabile testRE come un array. Questo perché testRE sta tornando come un array, per qualche motivo. L'output di:

My cow always gives milk

Cambia in:

always gives

1
Grazie, ho aggiunto un violino ( jsfiddle.net/MoscaPt/g5Lngjx8/2 ) per questo. / Johan
Mosca Pt,

4

Usa la seguente espressione regolare:

(?<=My cow\s).*?(?=\smilk)

Look Behind ?<=non è supportato in Javascript. Sarebbe il modo di farlo però.
Mark Carpenter Jr,

È supportato in JavaScript. Non è supportato in Safari e Mozilla (ancora), solo in Chrome e Opera.
Paul Strupeikis,

4

Trovo che regex sia noioso e dispendioso in termini di tempo data la sintassi. Dato che stai già utilizzando JavaScript, è più facile eseguire le seguenti operazioni senza regex:

const text = 'My cow always gives milk'
const start = `cow`;
const end = `milk`;
const middleText = text.split(start)[1].split(end)[0]
console.log(middleText) // prints "always gives"

2
Per me va bene! risposta fantastica perché è davvero molto semplice! :)
Andrew Irwin,


0

Il metodo match () cerca una stringa per una corrispondenza e restituisce un oggetto Array.

// Original string
var str = "My cow always gives milk";

// Using index [0] would return<br/>
// "**cow always gives milk**"
str.match(/cow(.*)milk/)**[0]**


// Using index **[1]** would return
// "**always gives**"
str.match(/cow(.*)milk/)[1]

0

Compito

Estrai la sottostringa tra due stringhe (escludendo queste due stringhe)

Soluzione

let allText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum";
let textBefore = "five centuries,";
let textAfter = "electronic typesetting";
var regExp = new RegExp(`(?<=${textBefore}\\s)(.+?)(?=\\s+${textAfter})`, "g");
var results = regExp.exec(allText);
if (results && results.length > 1) {
    console.log(results[0]);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.