Регулярний вираз для отримання рядка між двома рядками в JavaScript


166

Я знайшов дуже схожі пости, але я не можу повністю зрозуміти своє регулярне вираження.

Я намагаюся написати регулярний вираз, який повертає рядок, що знаходиться між двома іншими рядками. Наприклад: Я хочу отримати рядок, який знаходиться між рядками "корова" та "молоко".

Моя корова завжди дає молоко

повернеться

"завжди дає"

Ось вираз, який я зібрав поки що:

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

Однак це повертає рядок "корова завжди дає".


6
Я натрапив на це старе питання і хотів уточнити, чому testRE - це масив. test.match повертає масив з першим індексом як загальний збіг (therfor, рядок, який відповідає коров'ячому (. *) молоку), а потім, усі захоплені рядки, як (. *), якби був другий набір дужок, вони б то будьте в тестіRE [2]
Салькетер

4
Це рішення не працюватиме, якщо ви шукаєте рядок, що містить нові рядки. У такому випадку слід використовувати "STRING_ONE ([\\ s \\ S] *?) STRING_TWO". stackoverflow.com/questions/22531252/…
Michael.Lumley

лише для довідки про метод відповідності на MDN developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
vzR

Відповіді:


183

Шукаюча (?=частина (ця частина) не споживає ніяких даних. Це твердження нульової ширини (як і граничні перевірки та відставання).

Тут ви хочете регулярний матч, щоб споживати cowпорцію. Щоб захопити частину між ними, ви використовуєте групу захоплення (просто покладіть частину шаблону, який ви хочете зробити всередині дужок):

cow(.*)milk

Жодні головокруги не потрібні взагалі.


26
Коли я перевіряю це, наданий вираз Regex включає в себе і "корову", і "молоко" ...
TheCascadian

4
Цього кроку не вистачає. Коли ви отримаєте результат збігу, вам потрібно витягнути зібраний текст першої групи захоплення matched[1], а не весь відповідний текст matched[0].
Rory O'Kane

7
У Javascript вам потрібно використовувати, ([\s\S]*?)а не використовувати (.*?).
Qian Chen

7
Хоча це і є корисною технікою, вона була спростована, оскільки ІМХО це НЕ правильна відповідь на питання, оскільки вона включає "корову" та "молоко", про що заявив @TheCascadian
Алмір Кампос

@AlmirCampos - якщо я не помиляюся, немає способу зробити цю відповідність без відповідності "корова" та "молоко" (оскільки ви хочете відповідати тому, що знаходиться між цими двома). Проблема полягає не в самому RegEx, а в тому, як ви з ним впораєтеся згодом (як згадував Рорі О'Кане). Інакше ви могли б відповідати лише для навколишніх просторів - і це дасть вам ДУЖЕ неправильне повернення, чи не так?
народився

69

Регулярний вираз для отримання рядка між двома рядками в JavaScript

Найповнішим рішенням, яке буде працювати в переважній більшості випадків, є використання групи захоплення з ледачим зразком узгодження крапок . Тим НЕ менше, точка .в JavaScript регулярний вираз не відповідає символи перекладу рядків, так, що буде працювати в 100% випадків є [^]або [\s\S]/ [\d\D]/ [\w\W]конструкції.

ECMAScript 2018 та новіші сумісні рішення

У середовищах JavaScript, що підтримують ECMAScript 2018 , sмодифікатор дозволяє .відповідати будь-яким знакам, включаючи символи розриву рядків, а двигун regex підтримує вигляд із змінною довжиною. Отже, ви можете використовувати подібний регулярний вираз

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

В обох випадках поточне положення перевіряється на cowбудь-який 1/0 або більше пробілів після cow, потім будь-які символи 0+ якнайменше узгоджуються та споживаються (= додаються до значення відповідності), а потім milkперевіряється (з будь-яким 1/0 або більше пробілів перед цією підрядкою).

Сценарій 1: Однолінійний ввід

Цей та всі інші сценарії нижче підтримуються всіма середовищами JavaScript. Дивіться приклади використання внизу відповіді.

cow (.*?) milk

cowзнайдеться спочатку, потім пробіл, а потім будь-які символи 0+, крім знаків розриву рядків, якнайменше, наскільки *?це лінивий кількісний показник, потрапляють у групу 1, а потім пробіл з milkобов'язковим послідованням (і ці збігаються і споживаються теж ).

Сценарій 2: багаторядковий вхід

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

Тут cowі пробіл узгоджується спочатку, потім будь-які 0+ символів якнайменше співпадають і фіксуються в групу 1, а потім пробіл із milkзіставляються.

Сценарій 3: збіги, що перекриваються

Якщо у вас є такий рядок, >>>15 text>>>67 text2>>>і вам потрібно отримати 2 матчі між >>>+ number+ whitespaceі >>>, ви не можете використовувати, />>>\d+\s(.*?)>>>/gоскільки це знайде лише 1 матч через те, що >>>раніше 67вже витрачено при пошуку першого матчу. Ви можете скористатися позитивною підказкою для перевірки наявності тексту, не фактично "погризавши" його (тобто додаючи до збігу):

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

Дивіться онлайн регулярний вираз демо приносить text1і text2в групі знайдено 1 зміст.

Також див. Як отримати всі можливі збіги, що перетинаються для рядка .

Міркування щодо продуктивності

Лінивий шаблон узгодження точок ( .*?) всередині шаблонів регулярних виразів може сповільнити виконання сценарію, якщо буде введено дуже довге введення У багатьох випадках техніка розкручування допомагає більшою мірою. Намагаючись схопити все між cowі milkз "Their\ncow\ngives\nmore\nmilk", ми бачимо, що нам просто потрібно відповідати всі рядки, які не починаються з milk, таким чином, замість цього cow\n([\s\S]*?)\nmilkми можемо використовувати:

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

Перегляньте демонстраційну виразку (якщо є \r\n, використовуйте /cow\r?\n(.*(?:\r?\n(?!milk$).*)*)\r?\nmilk/gm). За допомогою цього невеликого тестового рядка приріст продуктивності незначний, але при дуже великому тексті ви відчуєте різницю (особливо, якщо рядки довгі та перерви рядків не дуже численні).

Зразок використання регулярного вираження в 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);

Використання сучасного String#matchAllметоду

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

Ось регулярний вираз, який захопить те, що знаходиться між коровою та молоком (без провідного / відсталого місця):

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

Приклад: http://jsfiddle.net/entropo/tkP74/


17
  • Вам потрібно захопити .*
  • Ви можете (але не обов'язково) зробити .*нонгідер
  • Тут справді немає необхідності в пошуку.

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

У цьому конкретному випадку, якщо воно було б жадібним, воно доходило б до кінця та відмови (імовірно).
Бен

9

Обрана відповідь для мене не спрацювала ... хм ...

Просто додайте пробіл після коров’яку та / або перед молоком, щоб обрізати місця з "завжди дає"

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

введіть тут опис зображення


Вам не потрібно коментувати власну відповідь, просто відредагуйте її.
Коді G

Look Behind ?<=не підтримується в JavaScript.
Марк Карпентер-молодший

@MarkCarpenterJr, якщо ви протестували його через regextester.com , ви отримаєте цей підказку. Схоже, що сайт вибудував свої правила зі старої специфікації. Зараз підтримується Lookbehind. Дивіться stackoverflow.com/questions/30118815/… І шаблон добре працює з сучасними браузерами без помилок. Спробуйте цю перевірку замість regex101.com
duduwe

@ CodyG.ah так. зрозумів.
duduwe

8

Я зміг отримати те, що мені потрібно, використовуючи рішення Мартіньо Фернандеса нижче. Код:

var test = "My cow always gives milk";

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

Ви помітите, що я попереджую змінну testRE як масив. Це тому, що testRE чомусь повертається як масив. Вихід від:

My cow always gives milk

Зміни в:

always gives

1
Дякую, я додав для нього загадку ( jsfiddle.net/MoscaPt/g5Lngjx8/2 ). / Йохан
Моска Пт

4

Просто використовуйте наступний регулярний вираз:

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

Look Behind ?<=не підтримується в JavaScript. Це був би спосіб, як це зробити.
Марк Карпентер-молодший

Він підтримується в JavaScript. Він не підтримується в Safari та Mozilla (поки), лише в Chrome і Opera.
Пол Струпейкіс

3

Я вважаю, що регулярний вираз є втомливим і трудомістким, враховуючи синтаксис. Оскільки ви вже використовуєте javascript, простіше зробити наступне без регулярного вираження:

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
Для мене працює! фантастична відповідь, тому що це просто дуже просто! :)
Ендрю Ірвін


0

Метод match () шукає рядок відповідності та повертає об’єкт 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

Завдання

Витягнути підрядку між двома рядками (за винятком цих двох рядків)

Рішення

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]);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.