剖析表達式
一個算術表達式包含 2 個數字和中間的一個運算子,例如
1 + 21.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;