返回课程

解析表达式

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

  • 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+)? – 最后一个十进制部分不存在,因此未定义)

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

完全匹配(数组的第一个项目)可以通过移位数组 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;