在 Javascript 中获取两个字符串之间的字符串的正则表达式

2022-08-30 00:48:31

我发现了非常相似的帖子,但我不能在这里完全得到我的正则表达式。

我正在尝试编写一个正则表达式,该表达式返回一个位于其他两个字符串之间的字符串。例如:我想获取位于字符串“cow”和“milk”之间的字符串。

我的奶牛总是给牛奶

会再来的

“总是给予”

以下是我到目前为止拼凑起来的表达式:

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

但是,这将返回字符串“牛总是给予”。


答案 1

前瞻(该部分)不消耗任何输入。它是一个零宽度断言(边界检查和观察后缀也是如此)。(?=

你想要一个常规的比赛在这里,消费部分。要捕获中间的部分,请使用捕获组(只需将要捕获的模式部分放在括号内):cow

cow(.*)milk

根本不需要任何前瞻。


答案 2

在 JavaScript 中获取两个字符串之间的字符串的正则表达式

在绝大多数情况下可行的最完整的解决方案是使用具有惰性点匹配模式捕获组。但是,JavaScript 正则表达式中的点与换行符不匹配,因此,在 100% 情况下有效的是 or // 构造。.[^][\s\S][\d\D][\w\W]

ECMAScript 2018 和更新的兼容解决方案

在支持 ECMAScript 2018 的 JavaScript 环境中,修饰符允许匹配任何字符,包括换行符,并且正则表达式引擎支持可变长度的查找后缀。因此,您可以使用正则表达式,例如s.

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

在这两种情况下,都会在 之后使用任何 1/0 或更多空格检查当前位置,然后匹配和使用尽可能少的任何 0+ 个字符(=添加到匹配值中),然后检查(在此子字符串之前使用任何 1/0 或更多空格)。cowcowmilk

方案 1:单行输入

所有 JavaScript 环境都支持此方案以及下面的所有其他方案。请参阅答案底部的用法示例。

cow (.*?) milk

cow首先找到一个空格,然后是一个空格,然后是除换行符以外的任何0多个字符,尽可能少地使用懒惰量词,被捕获到组1中,然后一个空格必须与后面(并且这些字符也被匹配和使用)。*?milk

方案 2:多行输入

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

在这里,首先匹配一个空格,然后将尽可能少的0多个字符匹配并捕获到组1中,然后匹配一个空格。cowmilk

方案 3:重叠匹配项

如果你有一个类似这样的字符串,并且你需要在 ++ 和 之间获得 2 个匹配项,则不能使用 />>>\d+\s(.*?)>>> /g,因为这只会找到 1 个匹配项,因为在找到第一个匹配项时,之前项已被消耗。您可以使用正面的 lookahead 来检查文本是否存在,而不会实际“吞噬”它(即附加到匹配项上):>>>15 text>>>67 text2>>>>>>numberwhitespace>>>>>>67

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

请参阅在线正则表达式演示,并找到组 1 内容。text1text2

另请参阅如何获取字符串的所有可能的重叠匹配项

性能注意事项

如果给出很长的输入,正则表达式模式中的惰性点匹配模式 () 可能会减慢脚本执行速度。在许多情况下,展开循环技术在更大程度上有所帮助。试图从 中获取所有之间和从 中获取所有内容,我们看到我们只需要匹配所有不以 开头的行,因此,而不是 cow\n([\s\S]*?)\n牛奶,我们可以使用:.*?cowmilk"Their\ncow\ngives\nmore\nmilk"milk

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