Wyrażenie regularne do pobrania ciągu między dwoma ciągami w JavaScript


166

Znalazłem bardzo podobne posty, ale nie mogę znaleźć tutaj mojego wyrażenia regularnego.

Próbuję napisać wyrażenie regularne, które zwraca ciąg, który znajduje się między dwoma innymi ciągami. Na przykład: chcę uzyskać ciąg, który znajduje się między ciągami „krowa” i „mleko”.

Moja krowa zawsze daje mleko

wróci

„zawsze daje”

Oto wyrażenie, które stworzyłem do tej pory:

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

Jednak zwraca to ciąg „krowa zawsze daje”.


6
Natknąłem się na to stare pytanie i chciałem wyjaśnić, dlaczego testRE jest tablicą. test.match zwraca tablicę z pierwszym indeksem jako całkowitym dopasowaniem (therfor, ciąg pasujący do mleka krowiego (. *)), a następnie wszystkie uwięzione ciągi, takie jak (. *), gdyby istniał drugi zestaw nawiasów, następnie być w teścieRE [2]
Salketer

4
To rozwiązanie nie zadziała, jeśli szukasz ciągu zawierającego znaki nowej linii. W takim przypadku należy użyć „STRING_ONE ([\\ s \\ S] *?) STRING_TWO”. stackoverflow.com/questions/22531252/…
Michael.Lumley

tylko w celach informacyjnych metoda dopasowania w MDN developer.mozilla.org/en/docs/Web/JavaScript/Reference/ ...
vzR

Odpowiedzi:


183

Lookahead (ta (?=część) nie zużywa żadnych danych wejściowych. Jest to asercja o zerowej szerokości (podobnie jak sprawdzanie granic i lookbehinds).

Chcesz tutaj regularne zapałki, aby skonsumować cowporcję. Aby uchwycić część pomiędzy, użyj grupy przechwytywania (po prostu umieść fragment wzorca, który chcesz uchwycić, w nawiasach):

cow(.*)milk

W ogóle nie są potrzebne żadne lookahead.


26
Kiedy to
testuję

4
Brakuje kroku. Kiedy otrzymasz wynik dopasowania, musisz wyodrębnić dopasowany tekst z pierwszej grupy przechwytywania za pomocą matched[1], a nie cały dopasowany tekst matched[0].
Rory O'Kane

7
W Javascript w rzeczywistości musisz użyć ([\s\S]*?)zamiast (.*?).
Qian Chen,

7
Chociaż jest to przydatna technika, została odrzucona, ponieważ IMHO NIE jest to właściwa odpowiedź na pytanie, ponieważ zawiera „krowę” i „mleko”, jak stwierdził @TheCascadian
Almir Campos,

@AlmirCampos - jeśli się nie mylę, to nie da się tego dopasować bez dopasowania „krowy” i „mleka” (ponieważ chcesz dopasować to, co jest pomiędzy tymi dwoma). Problem nie leży w samym wyrażeniu regularnym, ale w tym, jak sobie z nim radzisz (jak wspomniał Rory O'Kane). W przeciwnym razie mógłbyś dopasować tylko do otaczających przestrzeni - a to dałoby BARDZO zły wynik, prawda?
urodził się

69

Wyrażenie regularne do pobrania ciągu między dwoma ciągami w JavaScript

Najbardziej kompletnym rozwiązaniem, które będzie działać w zdecydowanej większości przypadków, jest użycie grupy przechwytywania z leniwym wzorem dopasowywania kropek . Jednak kropka .w wyrażeniu regularnym JavaScript nie pasuje do znaków końca wiersza, więc to, co zadziała w 100%, to konstrukcja [^]lub [\s\S]/ [\d\D]/ [\w\W].

ECMAScript 2018 i nowsze kompatybilne rozwiązanie

W środowisku JavaScript podtrzymujących ECMAScript 2018 , smodyfikatora umożliwia .dopasowanie wszystkich char tym włamaniowych linii znaków i regex wsporniki silnika lookbehinds o zmiennej długości. Możesz więc użyć wyrażenia regularnego, takiego jak

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

W obu przypadkach bieżąca pozycja jest sprawdzana za cowpomocą 1/0 lub więcej białych znaków po cow, a następnie dowolne 0+ znaków tak mało, jak to możliwe, jest dopasowywane i konsumowane (= dodawane do wartości dopasowania), a następnie milksprawdzane pod kątem (z dowolnymi 1/0 lub więcej białych znaków przed tym podciągiem).

Scenariusz 1: wejście jednoliniowe

Ten i wszystkie inne poniższe scenariusze są obsługiwane przez wszystkie środowiska JavaScript. Zobacz przykłady użycia na dole odpowiedzi.

cow (.*?) milk

cowznaleziono, następnie miejsce, wtedy każdy 0+ znaki inne niż linia przerwy znaków, tak mało jak to możliwe, jak *?to leniwa kwantyfikator, są ujęte w grupy 1, a następnie z przestrzeni milkmuszą przestrzegać (oraz są dopasowane i spożywane zbyt ).

Scenariusz 2: wejście wieloliniowe

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

Tutaj cowi spacja są najpierw dopasowywane, następnie dowolne 0+ znaków, jak najmniej, jest dopasowywanych i przechwytywanych do grupy 1, a następnie spacja z milkjest dopasowywana.

Scenariusz 3: Pokrywające się mecze

Jeśli masz taki ciąg >>>15 text>>>67 text2>>>i chcesz uzyskać 2 dopasowania pomiędzy >>>+ number+ whitespacei >>>, nie możesz użyć, />>>\d+\s(.*?)>>>/gponieważ spowoduje to znalezienie tylko 1 dopasowania, ponieważ >>>poprzednia 67jest już zużyta po znalezieniu pierwszego dopasowania. Możesz użyć pozytywnego lookahead, aby sprawdzić obecność tekstu bez faktycznego „pożerania” go (tj. Dołączania do dopasowania):

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

Zobacz demo regex Internecie plonowanie text1i text2jako Grupa 1 znalezionych zawartość.

Zobacz także Jak uzyskać wszystkie możliwe nakładające się dopasowania dla ciągu .

Uwagi dotyczące wydajności

Leniwy wzorzec dopasowania kropek ( .*?) wewnątrz wzorców regex może spowolnić wykonywanie skryptu, jeśli podano bardzo długie dane wejściowe. W wielu przypadkach technika rozwijania pętli pomaga w większym stopniu. Próbując złapać wszystko pomiędzy cowi milkz "Their\ncow\ngives\nmore\nmilk", widzimy, że musimy dopasować wszystkie linie, które nie zaczynają się od milk, więc zamiast cow\n([\s\S]*?)\nmilkmożemy użyć:

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

Zobacz demo wyrażeń regularnych (jeśli istnieje \r\n, użyj /cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm). W przypadku tego małego ciągu testowego wzrost wydajności jest pomijalny, ale w przypadku bardzo dużego tekstu poczujesz różnicę (zwłaszcza jeśli linie są długie, a znaki końca linii nie są zbyt liczne).

Przykładowe użycie wyrażenia regularnego w 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);

Stosując nowoczesną String#matchAllmetodę

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

Oto wyrażenie regularne, które przechwytuje to, co jest między krową a mlekiem (bez spacji na początku / końcu):

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

Przykład: http://jsfiddle.net/entropo/tkP74/


17
  • Musisz przechwycić .*
  • Możesz (ale nie musisz) robić tego, co nie jest .*graniem
  • Naprawdę nie ma potrzeby patrzenia w przyszłość.

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

W tym konkretnym przypadku, gdyby był chciwy, doszedłby do końca i zawrócił (prawdopodobnie).
Ben,

9

Wybrana odpowiedź nie działa dla mnie ... hmm ...

Wystarczy dodać spację po krowie i / lub przed mlekiem, aby usunąć spacje z „zawsze daje”

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

wprowadź opis obrazu tutaj


Nie musisz komentować własnej odpowiedzi, po prostu ją edytuj.
Cody G

Funkcja Look Behind ?<=nie jest obsługiwana w JavaScript.
Mark Carpenter Jr

@MarkCarpenterJr, jeśli przetestowałeś go za pośrednictwem regextester.com , otrzymasz tę wskazówkę. Wygląda na to, że serwis oparł swoje zasady na starszej specyfikacji. Funkcja Lookbehind jest teraz obsługiwana. Zobacz stackoverflow.com/questions/30118815/… A wzór działa dobrze w nowoczesnych przeglądarkach bez błędów. Spróbuj tego narzędzia zamiast tego regex101.com
duduwe

@ CodyG.ah tak. Rozumiem.
duduwe,

8

Udało mi się uzyskać to, czego potrzebowałem, korzystając z rozwiązania Martinho Fernandesa poniżej. Kod to:

var test = "My cow always gives milk";

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

Zauważysz, że ostrzegam zmienną testRE jako tablicę. Dzieje się tak, ponieważ z jakiegoś powodu testRE zwraca jako tablicę. Wynik z:

My cow always gives milk

Zmiany w:

always gives

1
Dzięki, dodałem do niego skrzypce ( jsfiddle.net/MoscaPt/g5Lngjx8/2 ). / Johan
Mosca Pt

4

Po prostu użyj następującego wyrażenia regularnego:

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

Funkcja Look Behind ?<=nie jest obsługiwana w JavaScript. Byłby to jednak sposób na zrobienie tego.
Mark Carpenter Jr

Jest obsługiwany w JavaScript. Nie jest obsługiwany w Safari i Mozilli (jeszcze), tylko w Chrome i Opera.
Paul Strupeikis

3

Uważam, że regex jest żmudny i czasochłonny, biorąc pod uwagę składnię. Ponieważ używasz już javascript, łatwiej jest wykonać następujące czynności bez wyrażenia regularnego:

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
Pracuje dla mnie! fantastyczna odpowiedź, bo to naprawdę proste! :)
Andrew Irwin


0

Metoda match () przeszukuje ciąg znaków pod kątem dopasowania i zwraca obiekt 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

Zadanie

Wyodrębnij podciąg między dwoma ciągami (z wyłączeniem tych dwóch ciągów)

Rozwiązanie

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]);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.