回到课程

解析表达式

一个算术表达式由 2 个数字和一个它们之间的运算符组成,例如:

  • 1 + 2
  • 1.2 * 3.4
  • -3 / -6
  • -2 - 2

运算符为 "+""-""*""/" 中之一。

在开头、之间的部分或末尾可能有额外的空格。

创建一个函数 parse(expr),它接受一个表达式作为参数,并返回一个包含 3 个元素的数组:

  1. 第一个数字
  2. 运算符
  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* 分隔:

  1. -?\d+(\.\d+)? —— 第一个数字,
  2. [-+*/] —— 运算符,
  3. -?\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+)? —— 第二个数字的小数部分不存在,所以这里是 undefined)

我们只想要数字和运算符,不需要完全匹配的以及小数部分结果,所以让我们稍微“清理”一下结果。

我们可以使用数组的 shift 方法 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