Regex/Javascript,将字符串转换成绝对值|x|到 abs(x)
Regex/Javascript, convert strings with absolute values |x| to abs(x)
我正在尝试使用 Javascript 中的 Regex 转换一些包含绝对值的数学相关字符串。
我想将所有出现的 |foo|
转换为 abs(foo)
.
鉴于字符也可以嵌套,我如何检测字符是打开还是关闭?
基本上,我想将所有出现的开盘 |
转换为 abs(
,并将所有闭盘 |
转换为 )
。竖线之间的内容不变。
一些可能的输入和期望的输出示例:
|x|+12
abs(x)+12
|x|+12+|x+2|
abs(x)+12+abs(x+2)
|x|+|x+|z||
abs(x)+abs(x+abs(z))
有什么想法吗?
有支持嵌套的正则表达式方言,JavaScript不是其中之一。但是,您可以分步执行此操作:
- 用嵌套级别(+1、-1,从左到右)标记
|
- 根据标签从左到右,从低到高识别同一层的开始和结束
|
- 在输入不平衡的情况下清理遗留标签
具有最多 3 个级别的测试用例的功能代码(代码适用于任何级别):
function fixAbs(str) {
const startTag = '{{s%L%}}';
const endTag = '{{e%L%}}';
const absRegex = /\{\{s(\d+)\}\}(.*?)\{\{e\}\}/g;
let level = 0;
str = str
.replace(/ /g, '') // remove all spaces
.replace(/(\|*)?(\w+)(\|*)?/g, function(m, c1, c2, c3) {
// regex matches variables with all leading and trailing `|`s
let s = c2;
if(c1) {
// add a start tag to each leading `|`: `{{s0}}`, `{{s1}}`, ...
// and post-increase level
s = '';
for(let i = 0; i < c1.length; i++) {
s += startTag.replace(/%L%/, level++);
}
s += c2;
}
if(c3) {
// decrease level,
// and add a end tag to each trailing `|`: `{{e2}}`, `{{e1}}`, ...
for(let i = 0; i < c3.length; i++) {
s += endTag.replace(/%L%/, --level);
}
}
return s;
});
// find matching start and end tag from left to right,
// repeat for each level
while(str.match(absRegex)) {
str = str.replace(absRegex, function(m, c1, c2, c3) {
return 'abs(' + c2 + ')';
});
}
// clean up tags in case of unbalanced input
str = str.replace(/\{\{[se]-?\d+\}\}/g, '|');
return str;
}
const testCases = [
'|x|+12',
'|x|+|y+|z||',
'|x|+||y|+z|',
'|x|+|x+|y|+z|',
'|x|+|x+|y+|t||+z|',
'|x|+12+|2+x|',
'|x|+12+|x+2|'
].forEach(str => {
let result = fixAbs(str);
console.log('"' + str + '" ==> "' + result + '"');
});
输出:
"|x|+12" ==> "abs(x)+12"
"|x|+|y+|z||" ==> "abs(x)+abs(y+abs(z))"
"|x|+||y|+z|" ==> "abs(x)+abs(abs(y)+z)"
"|x|+|x+|y|+z|" ==> "abs(x)+abs(x+abs(y)+z)"
"|x|+|x+|y+|t||+z|" ==> "abs(x)+abs(x+abs(y+abs(t))+z)"
"|x|+12+|2+x|" ==> "abs(x)+12+abs(2+x)"
"|x|+12+|x+2|" ==> "abs(x)+12+abs(x+2)"
为了清晰起见,代码用注释进行了注释。
上的 TWiki 博客
我正在尝试使用 Javascript 中的 Regex 转换一些包含绝对值的数学相关字符串。
我想将所有出现的 |foo|
转换为 abs(foo)
.
鉴于字符也可以嵌套,我如何检测字符是打开还是关闭?
基本上,我想将所有出现的开盘 |
转换为 abs(
,并将所有闭盘 |
转换为 )
。竖线之间的内容不变。
一些可能的输入和期望的输出示例:
|x|+12
abs(x)+12
|x|+12+|x+2|
abs(x)+12+abs(x+2)
|x|+|x+|z||
abs(x)+abs(x+abs(z))
有什么想法吗?
有支持嵌套的正则表达式方言,JavaScript不是其中之一。但是,您可以分步执行此操作:
- 用嵌套级别(+1、-1,从左到右)标记
|
- 根据标签从左到右,从低到高识别同一层的开始和结束
|
- 在输入不平衡的情况下清理遗留标签
具有最多 3 个级别的测试用例的功能代码(代码适用于任何级别):
function fixAbs(str) {
const startTag = '{{s%L%}}';
const endTag = '{{e%L%}}';
const absRegex = /\{\{s(\d+)\}\}(.*?)\{\{e\}\}/g;
let level = 0;
str = str
.replace(/ /g, '') // remove all spaces
.replace(/(\|*)?(\w+)(\|*)?/g, function(m, c1, c2, c3) {
// regex matches variables with all leading and trailing `|`s
let s = c2;
if(c1) {
// add a start tag to each leading `|`: `{{s0}}`, `{{s1}}`, ...
// and post-increase level
s = '';
for(let i = 0; i < c1.length; i++) {
s += startTag.replace(/%L%/, level++);
}
s += c2;
}
if(c3) {
// decrease level,
// and add a end tag to each trailing `|`: `{{e2}}`, `{{e1}}`, ...
for(let i = 0; i < c3.length; i++) {
s += endTag.replace(/%L%/, --level);
}
}
return s;
});
// find matching start and end tag from left to right,
// repeat for each level
while(str.match(absRegex)) {
str = str.replace(absRegex, function(m, c1, c2, c3) {
return 'abs(' + c2 + ')';
});
}
// clean up tags in case of unbalanced input
str = str.replace(/\{\{[se]-?\d+\}\}/g, '|');
return str;
}
const testCases = [
'|x|+12',
'|x|+|y+|z||',
'|x|+||y|+z|',
'|x|+|x+|y|+z|',
'|x|+|x+|y+|t||+z|',
'|x|+12+|2+x|',
'|x|+12+|x+2|'
].forEach(str => {
let result = fixAbs(str);
console.log('"' + str + '" ==> "' + result + '"');
});
输出:
"|x|+12" ==> "abs(x)+12"
"|x|+|y+|z||" ==> "abs(x)+abs(y+abs(z))"
"|x|+||y|+z|" ==> "abs(x)+abs(abs(y)+z)"
"|x|+|x+|y|+z|" ==> "abs(x)+abs(x+abs(y)+z)"
"|x|+|x+|y+|t||+z|" ==> "abs(x)+abs(x+abs(y+abs(t))+z)"
"|x|+12+|2+x|" ==> "abs(x)+12+abs(2+x)"
"|x|+12+|x+2|" ==> "abs(x)+12+abs(x+2)"
为了清晰起见,代码用注释进行了注释。
上的 TWiki 博客