Regulärer Ausdruck, um eine Zeichenfolge zwischen zwei Zeichenfolgen in Javascript abzurufen

166

Ich habe sehr ähnliche Beiträge gefunden, aber ich kann meinen regulären Ausdruck hier nicht ganz finden.

Ich versuche, einen regulären Ausdruck zu schreiben, der eine Zeichenfolge zurückgibt, die zwischen zwei anderen Zeichenfolgen liegt. Zum Beispiel: Ich möchte die Zeichenfolge erhalten, die sich zwischen den Zeichenfolgen "Kuh" und "Milch" befindet.

Meine Kuh gibt immer Milch

würden zurückkehren

"gibt immer"

Hier ist der Ausdruck, den ich bisher zusammengesetzt habe:

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

Dies gibt jedoch die Zeichenfolge "Kuh gibt immer" zurück.

phil
quelle
6
Ich bin über diese alte Frage gestolpert und wollte klären, warum testRE ein Array ist. test.match gibt ein Array mit dem ersten Index als Gesamtübereinstimmung zurück (daher die Zeichenfolge, die mit Kuhmilch (. *) übereinstimmt) und dann alle eingeschlossenen Zeichenfolgen wie die (. *), wenn es einen zweiten Satz von Klammern gäbe dann sei in testRE [2]
Salketer
4
Diese Lösung funktioniert nicht, wenn Sie nach einer Zeichenfolge suchen, die Zeilenumbrüche enthält. In einem solchen Fall sollten Sie "STRING_ONE ([\\ s \\ S] *?) STRING_TWO" verwenden. stackoverflow.com/questions/22531252/…
Michael.Lumley
Nur als Referenz die Match-Methode auf MDN developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
vzR

Antworten:

183

Ein Lookahead (dieser (?=Teil) verbraucht keine Eingabe. Es handelt sich um eine Behauptung mit einer Breite von Null (ebenso wie Grenzprüfungen und Lookbehinds).

Sie möchten hier ein reguläres Match, um die cowPortion zu verbrauchen . Um den Teil dazwischen zu erfassen, verwenden Sie eine Erfassungsgruppe (setzen Sie einfach den Teil des Musters, den Sie erfassen möchten, in Klammern):

cow(.*)milk

Es werden überhaupt keine Lookaheads benötigt.

R. Martinho Fernandes
quelle
26
Wenn ich dies
teste
4
Hier fehlt ein Schritt. Wenn Sie das Ergebnis der Übereinstimmung erhalten, müssen Sie den übereinstimmenden Text der ersten Erfassungsgruppe mit extrahieren matched[1], nicht den gesamten übereinstimmenden Text mit matched[0].
Rory O'Kane
7
In Javascript müssen Sie tatsächlich ([\s\S]*?)eher als verwenden (.*?).
Qian Chen
7
Obwohl dies eine nützliche Technik ist, wurde sie abgelehnt, da dies meiner Meinung nach NICHT die richtige Antwort auf die Frage ist, da sie "Kuh" und "Milch" enthält, wie von @TheCascadian
Almir Campos
@AlmirCampos - Wenn ich mich nicht irre, gibt es keine Möglichkeit, dieses Match zu machen, ohne "Kuh" und "Milch" zu finden (da Sie übereinstimmen möchten, was zwischen diesen beiden liegt). Das Problem liegt nicht in der RegEx selbst, sondern darin, wie Sie danach damit umgehen (wie von Rory O'Kane erwähnt). Andernfalls könnten Sie nur für umliegende Räume passen - und das würde Ihnen eine SEHR falsche Rendite bringen, nicht wahr?
geboren
69

Regulärer Ausdruck, um eine Zeichenfolge zwischen zwei Zeichenfolgen in JavaScript abzurufen

Die vollständigste Lösung, die in den allermeisten Fällen funktioniert, ist die Verwendung einer Erfassungsgruppe mit einem Lazy Dot Matching-Muster . Ein Punkt .in JavaScript-Regex stimmt jedoch nicht mit Zeilenumbruchzeichen überein. In 100% der Fälle funktioniert also ein [^]oder [\s\S]/ [\d\D]/ [\w\W]Konstrukt.

ECMAScript 2018 und neuere kompatible Lösung

In JavaScript-Umgebungen, die ECMAScript 2018 unterstützen , können mit dem sModifikator .alle Zeichen einschließlich Zeilenumbruchzeichen abgeglichen werden, und die Regex-Engine unterstützt Lookbehinds mit variabler Länge. Sie können also einen regulären Ausdruck wie verwenden

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 beiden Fällen wird die aktuelle Position cowmit 1/0 oder mehr Leerzeichen danach überprüft cow, dann werden alle 0+ Zeichen so wenig wie möglich abgeglichen und verbraucht (= zum Übereinstimmungswert hinzugefügt) und dann milküberprüft (mit einem beliebigen) 1/0 oder mehr Leerzeichen vor diesem Teilstring).

Szenario 1: Einzeilige Eingabe

Dieses und alle anderen unten aufgeführten Szenarien werden von allen JavaScript-Umgebungen unterstützt. Siehe Verwendungsbeispiele am Ende der Antwort.

cow (.*?) milk

cowzuerst, dann ein Leerzeichen, dann alle 0+ Zeichen außer Zeilenumbruch Zeichen, so wenig wie möglich zu finden ist wie *?ein fauler quantifier ist, werden in Gruppe 1 erfasst und dann ein Raum mit milkfolgen müssen (und die aufeinander abgestimmt sind und verbraucht auch ).

Szenario 2: Mehrzeilige Eingabe

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

Hier wird zuerst cowein Leerzeichen abgeglichen, dann werden möglichst wenige 0+ Zeichen abgeglichen und in Gruppe 1 erfasst, und dann wird ein Leerzeichen mit milkabgeglichen.

Szenario 3: Überlappende Übereinstimmungen

Wenn Sie eine Zeichenfolge wie >>>15 text>>>67 text2>>>und Sie müssen zwei Spiele bekommen in-zwischen >>>+ number+ whitespaceund >>>können Sie nicht verwenden , />>>\d+\s(.*?)>>>/gda dies nur ein Spiel aufgrund der Tatsache , finden die >>>zuvor 67bereits verbraucht bei der Suche nach der ersten Partie. Sie können einen positiven Lookahead verwenden , um die Textpräsenz zu überprüfen, ohne sie tatsächlich zu "verschlingen" (dh an das Spiel anzuhängen):

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

Siehe die Online-Regex-Demo mit text1undtext2 als Gruppe 1 Inhalte.

Siehe auch So erhalten Sie alle möglichen überlappenden Übereinstimmungen für eine Zeichenfolge .

Leistungsüberlegungen

Lazy Dot Matching Pattern ( .*?) in Regex-Mustern kann die Skriptausführung verlangsamen, wenn sehr lange Eingaben gemacht werden. In vielen Fällen hilft die Unroll-the-Loop-Technik in größerem Maße. Beim Versuch, alle zwischen cowund milkvon zu erfassen "Their\ncow\ngives\nmore\nmilk", sehen wir, dass wir nur alle Zeilen abgleichen müssen, die nicht mit beginnen. Daher können wir milkstattdessen cow\n([\s\S]*?)\nmilkFolgendes verwenden:

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

Siehe die Regex-Demo (falls vorhanden \r\n, verwenden /cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm). Mit dieser kleinen Testzeichenfolge ist der Leistungsgewinn vernachlässigbar, aber bei sehr großem Text werden Sie den Unterschied spüren (insbesondere wenn die Zeilen lang und die Zeilenumbrüche nicht sehr zahlreich sind).

Beispiel für die Verwendung von 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);

Mit der modernen String#matchAllMethode

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]));

Wiktor Stribiżew
quelle
51

Hier ist eine Regex, die erfasst, was zwischen Kuh und Milch liegt (ohne führenden / nachfolgenden Leerzeichen):

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

Ein Beispiel: http://jsfiddle.net/entropo/tkP74/

entropo
quelle
17
  • Sie müssen die erfassen .*
  • Sie können (müssen aber nicht) die .*Nongreedy machen
  • Der Lookahead ist wirklich nicht nötig.

    > /cow(.*?)milk/i.exec('My cow always gives milk');
    ["cow always gives milk", " always gives "]
Matt Ball
quelle
In diesem speziellen Fall würde es, wenn es gierig wäre, das Ende erreichen und (vermutlich) zurückgehen.
Ben
9

Die gewählte Antwort hat bei mir nicht funktioniert ... hmm ...

Fügen Sie einfach Platz nach der Kuh und / oder vor der Milch hinzu, um Leerzeichen von "immer gibt" zu entfernen.

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

Geben Sie hier die Bildbeschreibung ein

Duduwe
quelle
Sie müssen Ihre eigene Antwort nicht kommentieren, sondern nur bearbeiten.
Cody
Look Behind ?<=wird in Javascript nicht unterstützt.
Mark Carpenter Jr
@MarkCarpenterJr Wenn Sie es über regextester.com getestet haben , erhalten Sie diesen Hinweis. Es scheint, dass die Site ihre Regeln aus der älteren Spezifikation abgeleitet hat. Lookbehind wird jetzt unterstützt. Siehe stackoverflow.com/questions/30118815/…. Und das Muster funktioniert gut mit modernen Browsern ohne Fehler. Versuchen Sie diesen Checker stattdessen regex101.com
duduwe
@ CodyG.ah ja. Ich habs.
Duduwe
8

Mit der folgenden Lösung von Martinho Fernandes konnte ich das bekommen, was ich brauchte. Der Code lautet:

var test = "My cow always gives milk";

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

Sie werden feststellen, dass ich die Variable testRE als Array alarmiere. Dies liegt daran, dass testRE aus irgendeinem Grund als Array zurückgegeben wird. Die Ausgabe von:

My cow always gives milk

Änderungen in:

always gives
phil
quelle
1
Danke, ich habe eine Geige ( jsfiddle.net/MoscaPt/g5Lngjx8/2 ) dafür hinzugefügt . / Johan
Mosca Pt
4

Verwenden Sie einfach den folgenden regulären Ausdruck:

(?<=My cow\s).*?(?=\smilk)
Brandon
quelle
Look Behind ?<=wird in Javascript nicht unterstützt. Wäre aber der Weg, es zu tun.
Mark Carpenter Jr
Es wird in JavaScript unterstützt. Es wird (noch) nicht in Safari und Mozilla unterstützt, sondern nur in Chrome und Opera.
Paul Strupeikis
3

Ich finde Regex angesichts der Syntax mühsam und zeitaufwändig. Da Sie bereits Javascript verwenden, ist es einfacher, Folgendes ohne Regex zu tun:

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"
Chase Oliphant
quelle
2
Funktioniert bei mir! fantastische Antwort, weil es einfach ist! :)
Andrew Irwin
2

Wenn sich die Daten in mehreren Zeilen befinden, müssen Sie möglicherweise Folgendes verwenden:

/My cow ([\s\S]*)milk/gm

My cow always gives 
milk

Regex 101 Beispiel

Naresh Kumar
quelle
0

Die Methode match () durchsucht eine Zeichenfolge nach einer Übereinstimmung und gibt ein Array-Objekt zurück.

// 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]
Marc Antoni
quelle
0

Aufgabe

Teilzeichenfolge zwischen zwei Zeichenfolgen extrahieren (ohne diese beiden Zeichenfolgen)

Lösung

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]);
}
Wassili Bodnarchuk
quelle