动态正则表达式中占有量词的 JavaScript 替代方案

Posted

技术标签:

【中文标题】动态正则表达式中占有量词的 JavaScript 替代方案【英文标题】:JavaScript Alternative To Possessive Quantifiers In Dynamic Regex 【发布时间】:2015-08-14 21:12:59 【问题描述】:

我正在使用 javascript 从逗号分隔的成员字符串中提取“兄弟”子集,我称之为“世代”字符串。

打个比方,成员都来自同一代,但他们并不都是兄弟姐妹(来自同一个父母)。这是一个例子:

// This is the generation string to search
var generation  = 'ABAA,ABAB,ABAC,ABAD,ABBA,ACAA,ACAB,ACAD,AEAB,AEAD,AFAA';

// This is the member for whom to extract siblings (member included)
var member      = 'ACAA';

生成字符串及其成员具有以下特点:

每个成员的字符数与其他成员相同 字符串的所有成员都按字母排序 每组兄弟姐妹总是彼此相邻 兄弟姐妹是指除了最后一个字母外,字母组合完全相同的成员

继续示例...

// This is how I go about extracting the desired result: ACAA,ACAB,ACAD
var mParent     = member.substr(0, member.length - 1) ;
var mPattern    = mParent + '[A-Z]';
var mPattern    = '(.*)((' + mPattern + ')(,$1)*)(.*)'; // Trouble is here
var mRegex      = new RegExp(mPattern);
var mSiblings   = generation.replace(mRegex, '$2');

上面确定的问题点与构造模式中的正则表达式量词有关。如上所示,一切都设置为贪婪,因此 mSiblings 的值为:

ACAD

那只是最后一个成员。将 mPattern 更改为不那么贪婪以希望提取其他成员会产生以下结果

// Reluctant first expression yields ACAA
var mPattern = '(.*?)((' + mPattern + ')(,$1)*)(.*)'; 

// Reluctant last expression yields ACAD,AEAB,AEAD,AFAA
var mPattern = '(.*)((' + mPattern + ')(,$1)*)(.*?)'; 

// Reluctant first and last yields ACAA,ACAB,ACAD,AEAB,AEAD,AFAA
var mPattern = '(.*?)((' + mPattern + ')(,$1)*)(.*?)';

如果我可以使中间表达所有格,这将解决问题。像这样的:

// Make as many "middle" matches as possible by changing (,$1)* to (,$1)*+
var mPattern = '(.*?)((' + mPattern + ')(,$1)*+)(.*?)';

但正如我所读(并且有语法错误来证明),JavaScript 不支持所有格正则表达式量词。有人可以提出解决方案吗?谢谢你。

【问题讨论】:

【参考方案1】:

最明显的问题是$1。在正则表达式中,您将使用\1 而不是$1 来引用捕获组#1。正则表达式中的 (,$1)* 永远不会匹配任何内容。但无论如何,团体参考不会有任何好处。

当您在正则表达式中使用组引用时,您不会再次应用正则表达式的该部分,您只是匹配第一次匹配的相同内容。也就是说,(ACA[A-Z])(,\1)* 将匹配 ACAA,ACAA,但不匹配 ACAA,ACABACAA,ACAC。如果你想这样做,你需要重复实际的正则表达式:(ACA[A-Z])(,ACA[A-Z])*。由于您正在动态生成正则表达式,这应该不是问题。

请注意,这是整个正则表达式:ACA[A-Z](,ACA[A-Z])*。无需匹配您感兴趣的部分之前或之后的内容;这只会让工作变得更复杂(结果更令人困惑)。您可以直接访问匹配结果,而不是使用“替换”噱头:

var match = mRegex.exec(generation);
if (match != null) 
    mSiblings = match[0];

【讨论】:

如果每个成员的长度不固定,那么词边界是必要的。如果它已修复,则当前的解决方案有效。 @nhahtdh:你说的是真的,但 OP 确实说过所有成员的长度相同,除了最后一个字母外,兄弟姐妹的长度相同。虽然代码片段和声称的输出令人困惑,但实际的问题(大约文本的前三分之一)在这些点上很清楚。

以上是关于动态正则表达式中占有量词的 JavaScript 替代方案的主要内容,如果未能解决你的问题,请参考以下文章

所有格量词正则表达式的实际使用

在 JavaScript 正则表达式中使用 1+ 所有格量词时出现正则表达式错误

Javascript正则表达式量词:匹配零次或多次是啥意思

正则表达式 - 将 C# 正则表达式转换为 JavaScript 正则表达式的量词的目标无效

正则表达式无效的量词?

正则表达式之量词