[[JavaScript]]編、前半戦の最後は[[正規表現]]です。慣れるまでは難しいかもしれませんが、一度使いこなせるようになると、少ないコード量で多くの表現が可能になります。 ## Reference - [文字列 · JavaScript Primer \#jsprimer](https://jsprimer.net/basic/string/) ## Lesson [[正規表現]]は `/パターン/` で囲むか、[[RegExp (JavaScript)|RegExp]]インスタンスを生成して利用します。 ```js // 以下はどちらも同じ正規表現オブジェクト const regExp1 = /[0-9]{3}/; const regExp2 = new RegExp("[0-9]{3}"); ``` ### 判定 文字列が[[正規表現]]にマッチするかを確認するには[[RegExp.prototype.test (JavaScript)|RegExp.prototype.test]]を使います。 ```js const regExp = /[0-9]{3}/; regExp.test("10"); // false regExp.test("100"); // true regExp.test("1000"); // true ``` ### 抽出 文字列から[[正規表現]]にマッチする部分を取得するには[[String.prototype.match (JavaScript)|String.prototype.match]]を使います。 ```js const tasks = ` - [ ] 2025-02-18 task1 - [ ] 2025-02-19 task2 - [ ] 2025-02-19 task3 `; tasks.match(/\d{4}-\d{2}-\d{2}/g); // [ "2025-02-18", "2025-02-19", "2025-02-19" ] ``` マッチした文字列以外の詳細情報(マッチした位置など)が欲しい場合や、[[キャプチャグループ]]を使用する場合は[[String.prototype.matchAll (JavaScript)|String.prototype.matchAll]]を使います。 ```js const tasks = ` - [ ] 2025-02-18 task1 - [ ] 2025-02-19 task2 - [ ] 2025-02-19 task3 `; tasks.matchAll( /- \[ \] (?<date>\d{4}-\d{2}-\d{2}) (?<name>.+)/g, ).toArray(); /* [ [ "- [ ] 2025-02-18 task1", "2025-02-18", "task1", index: 1, input: "\n- [ ] 2025-02-18 task1\n- [ ] 2025-02-19 task2\n- [ ] 2025-02-19 task3\n", groups: [Object: null prototype] { date: "2025-02-18", name: "task1" } ], [ "- [ ] 2025-02-19 task2", "2025-02-19", "task2", index: 24, input: "\n- [ ] 2025-02-18 task1\n- [ ] 2025-02-19 task2\n- [ ] 2025-02-19 task3\n", groups: [Object: null prototype] { date: "2025-02-19", name: "task2" } ], [ "- [ ] 2025-02-19 task3", "2025-02-19", "task3", index: 47, input: "\n- [ ] 2025-02-18 task1\n- [ ] 2025-02-19 task2\n- [ ] 2025-02-19 task3\n", groups: [Object: null prototype] { date: "2025-02-19", name: "task3" } ] ] */ ``` > [!caution] > [[String.prototype.matchAll (JavaScript)|String.prototype.matchAll]]はイテレーターを返却するので、[[配列 (JavaScript)|配列]]に変換するため[[Iterator.prototype.toArray (JavaScript)|Iterator.prototype.toArray]]を使ったが、 #2025/02/19 時点では[[Safari]]に対応していない。プロダクトで実装するなら[[Array.from (JavaScript)|Array.from]]を使った方がよい。 ### 置換 文字列を置換するには[[String.prototype.replaceAll (JavaScript)|String.prototype.replaceAll]]を使います。 ```js "123 456 789".replaceAll(/\d/g, "7"); // "777 777 777" // 第1引数は正規表現でなくてもOK "私はたつおです。たつおはゴリラなんです。".replaceAll("たつお", "タツヲ"); // "私はタツヲです。タツヲはゴリラなんです。" ``` [[キャプチャグループ (JavaScript)|キャプチャグループ]]を使った置換もできます。 ```js const message = "2025年10月11日 2025年11月02日"; message.replaceAll(/(\d{4})年(\d{2})月(\d{2})日/g, "$1/$2/$3"); // 2025/10/11 2025/11/02 ``` ### 注意 [[String.prototype.matchAll (JavaScript)|String.prototype.matchAll]] や [[String.prototype.replaceAll (JavaScript)|String.prototype.replaceAll]] の第1引数に指定する正規表現パターンには `/パターン/g` のように [[gフラグ (JavaScript)|gフラグ]] を指定する必要があります。これを忘れるとエラーになります。 ## Mission 1 #🙂NORMAL [[正規表現]]の説明として**誤っているものをすべて**選び、その理由を説明してください。 1. `*` は任意の文字0文字以上である 2. `.+` は任意の文字1文字以上である 3. `[0-1][0-9]{3}` は1, 2, 3, ..., 9999, 10000の数字を表現できる 4. `[a-z]+` でアルファベットのみからなる単語すべてを表現できる %% 解答例 1, 3, 4が誤り。 1 `.*` じゃないといけない 2 `0000` ~ `1999` の範囲しか表現できない 4 小文字しか表現できない %% > [!hint]- Hint 1 > 正しいものは1つしかありません。 ## Mission 2 #😵HARD 以下のテストが通るように `swapColumns` を実装してください。 ```js function swapColumns(text) { // FIXME: 実装 } import { expect } from "jsr:@std/expect"; Deno.test("テーブルの2列目と3列目を入れ替える", () => { expect(swapColumns(` | col1 | col2 | col3 | col4 | | ---- | ---- | ---- | ---- | | val1 | val2 | val3 | val4 | | val1 | val2 | val3 | val4 | `)).toBe(` | col1 | col3 | col2 | col4 | | ---- | ---- | ---- | ---- | | val1 | val3 | val2 | val4 | | val1 | val3 | val2 | val4 | `); }); ``` %% 解答例 ```js function swapColumns(text) { return text.replaceAll(/\|(.*?)\|(.*?)\|(.*?)\|/g, "|$1|$3|$2|"); } ``` %% > [!hint]- Hint 1 > [[String.prototype.replaceAll (JavaScript)|String.prototype.replaceAll]]で[[キャプチャグループ (JavaScript)|キャプチャグループ]]を使います。 > [!hint]- Hint 2 > [[貪欲マッチング]] (`.*`) ではパターンが複雑になってしまうので、[[最短マッチング]]を使いましょう。 ## Mission 3 #😵HARD 以下のテストが通るように `createTasks` を実装してください。 ```js function createTasks(text) { // FIXME: 実装 } import { expect } from "jsr:@std/expect"; Deno.test("タスク文字列からタスクオブジェクトの配列を作成できる", () => { expect(createTasks(` - [ ] 2025-02-18 task1 - [ ] 2025-02-19 task2 - [ ] 2025-02-19 task3 `)).toEqual( [ { date: "2025-02-18", name: "task1" }, { date: "2025-02-19", name: "task2" }, { date: "2025-02-19", name: "task3" }, ], ); }); ``` %% 解答例 ```js function createTasks(text) { return text.matchAll( /- \[ \] (?<date>\d{4}-\d{2}-\d{2}) (?<name>.+)/g, ).toArray().map((x) => x.groups); } ``` %% > [!hint]- Hint 1 > [[String.prototype.matchAll (JavaScript)|String.prototype.matchAll]]を使います。 > [!hint]- Hint 2 > [[名前付きキャプチャグループ (JavaScript)|名前付きキャプチャグループ]]を使います。 --- *NEXT* >> [[📗TDQ-BOSS1 Promiseへの番人]]