剖析表達式
一個算術表達式包含 2 個數字和中間的一個運算子,例如
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 regexp /.../
中跳脫,我們稍後會執行此操作。
我們需要一個數字、一個運算子,然後再一個數字。它們之間可以有空格。
完整的正規表示式:-?\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;