解析表达式
算术表达式由两个数字和它们之间的运算符组成,例如
1 + 2
1.2 * 3.4
-3 / -6
-2 - 2
运算符是以下之一:"+"
、"-"
、"*"
或 "/"
。
在开头、结尾或各个部分之间可能存在额外的空格。
创建一个函数 parse(expr)
,它接收一个表达式并返回一个包含 3 个项目的数组
- 第一个数字。
- 运算符。
- 第二个数字。
例如
let [a, op, b] = parse("1.2 * 3.4");
alert(a); // 1.2
alert(op); // *
alert(b); // 3.4
数字的正则表达式为:-?\d+(\.\d+)?
。我们在之前的任务中创建了它。
运算符是 [-+*/]
。连字符 -
在方括号中排在首位,因为在中间它表示字符范围,而我们只需要字符 -
。
斜杠 /
应该在 JavaScript 正则表达式 /.../
中转义,我们稍后会这样做。
我们需要一个数字、一个运算符,然后是另一个数字。它们之间可以有可选的空格。
完整的正则表达式:-?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?
。
它有 3 个部分,它们之间用 \s*
分隔
-?\d+(\.\d+)?
– 第一个数字,[-+*/]
– 运算符,-?\d+(\.\d+)?
– 第二个数字。
为了使这些部分中的每一个成为结果数组的单独元素,让我们将它们括起来:(-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?)
。
实际应用
let regexp = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/;
alert( "1.2 + 12".match(regexp) );
结果包括
result[0] == "1.2 + 12"
(完全匹配)result[1] == "1.2"
(第一组(-?\d+(\.\d+)?)
– 第一个数字,包括小数部分)result[2] == ".2"
(第二组(\.\d+)?
– 第一个小数部分)result[3] == "+"
(第三组([-+*\/])
– 运算符)result[4] == "12"
(第四组(-?\d+(\.\d+)?)
– 第二个数字)result[5] == undefined
(第五组(\.\d+)?
– 最后一个十进制部分不存在,因此未定义)
我们只需要数字和运算符,不需要完全匹配或小数部分,所以让我们稍微“清理”一下结果。
完全匹配(数组的第一个项目)可以通过移位数组 result.shift()
来删除。
包含小数部分的组(数字 2 和 4)(.\d+)
可以通过在开头添加 ?:
来排除:(?:\.\d+)?
。
最终解决方案
function parse(expr) {
let regexp = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/;
let result = expr.match(regexp);
if (!result) return [];
result.shift();
return result;
}
alert( parse("-1.23 * 3.45") ); // -1.23, *, 3.45
作为使用非捕获组 ?:
的替代方案,我们可以像这样命名组
function parse(expr) {
let regexp = /(?<a>-?\d+(?:\.\d+)?)\s*(?<operator>[-+*\/])\s*(?<b>-?\d+(?:\.\d+)?)/;
let result = expr.match(regexp);
return [result.groups.a, result.groups.operator, result.groups.b];
}
alert( parse("-1.23 * 3.45") ); // -1.23, *, 3.45;