问题描述
花了半天时间阅读SO和其他地方的内容。
说我有一个字符串:
"a_b_c_d_e_f_g_1_2_3_4_5"
是否有一个可以基于两个匹配项构造结果的正则表达式?例如。构造一个由两个匹配项串联而成的字符串:第一个-在第3个和第5个之间,第二个在第8个和第10个下划线之间(不管它们之间还有多少个其他字符)?
以上示例的结果将是:
"d_e_2_3"
谢谢!
解决方法
是否可以使用单个正则表达式查找和提取两个子字符串?
不是,但是可以使用captures groups和String.prototype.replace
的正则表达式的组合。
OP用例的正则表达式可能看起来像这样...
(/^(?:[^_]*_){3}([^_]+_[^_]+)_(?:[^_]+_){3}([^_]+_[^_]+).*/)
...并且可以阅读如下...
- 有人想从String ...
^
的开头开始搜索。 - 下一个想要找到不是
_
...[^_]
以外的字符序列。
- 但是由于不能始终确保一个字符串不是以
_
开头,因此可以选择查找它,因此它变成了[^_]*
。 - 当然,这样的序列后面应带有
_
,因此前一个术语变成[^_]*_
。 - 由于该模式本应重复3次(
{3}
),因此需要将其分组((...)
),但不应将其捕获(?:
)...因此部分表达式变成^(?:[^_]*_){3}
,并且已经与OP的'a_b_c_'
示例中的'a_b_c_d_e_f_g_1_2_3_4_5'
相匹配。
- 现在,人们希望匹配一个非
_
字符序列,然后匹配一个_
,然后匹配一个非{_
字符序列,然后匹配一个_
。一个人想要捕获除最后一个_
以外的所有内容。因此,正则表达式的第二部分看起来像这样……([^_]+_[^_]+)_
。 - 第三部分与第一部分相似,不同之处在于第三部分肯定存在下一个不是
_
的字符(序列)。因此,正则表达式的第三部分看起来像这样……(?:[^_]+_){3}
。 - 第四部分是第二部分的确切副本...
([^_]+_[^_]+)
。 - 为了完全匹配该字符串,需要通过一个贪婪的通配推车来替换字符串的其余部分...
.
匹配任何内容...*
,以防仍然有匹配的内容 - 由于一个人可能还支持多行匹配,因此必须同时提供全局(
g
)和多行(m
)两个标志。
示例代码...
const regX = (/^(?:[^_]*_){3}([^_]+_[^_]+)_(?:[^_]+_){3}([^_]+_[^_]+).*/gm);
console.log(
'a_b_c_d_e_f_g_1_2_3_4_5'.replace(regX,'$1_$2')
);
console.log(
'_b_c_d_e_f_g_1_2_3_4_5'.replace(regX,'$1_$2')
);
console.log(
'b_c_d_e_f_g_1_2_3_4_5'.replace(regX,'$1_$2')
);
console.log(
'_c_d_e_f_g_1_2_3_4_5'.replace(regX,'$1_$2')
);
console.log([...
`a_b_c_d_e_f_g_1_2_3_4_5
_b_c_dd_ee_f_g_1_222_333_4_5
b_c_dd_ee_f_g_1_222_333_4_5
_c_dd_ee_ff_g_1_222_333_444_5
c_dd_ee_ff_g_1_222_333_444_55_66
_dd_ee_ff_gg_1_222_333_444_55_66`
.matchAll(regX)].map(([match,$1,$2]) => ($1 + '_' + $2))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }