置換とキャプチャ
このレッスンで分かること
replaceとreplaceAllの違いと、gフラグとの関係- 丸かっこのキャプチャグループと、置換文字列の中で使う
$1$2- 置換文字列では書けない加工を、置換関数で書く方法
置換とキャプチャ とは
当たった部分を書き換える操作です。丸かっこで囲んだ部分を取り出して並べ替えたり、関数で加工したりできます。
探すから、書き換えるへ
ここまでは「当たるか」「当たった文字は何か」を見てきました。最後は書き換えです。学習ログに 2026-04-01 と書いた日付を、表示するときだけ 2026年4月1日 に直したい、といった場面で使います。
一番単純な形は文字列同士の置き換えです。
const text = "作品は5件です";
console.log(text.replace("件", "作品"));
// 作品は5作品ですreplace は元の文字列を変えません。新しい文字列を返すだけです。戻り値を受け取り忘れると何も起きていないように見えます。
第 1 引数に正規表現を渡すと、形で探して書き換えられます。ただし g を付けないと最初の 1 件しか置き換わりません。
const log = "2026-04-01 と 2026-12-25 に公開";
console.log(log.replace(/\d{4}-\d{2}-\d{2}/, "[日付]"));
// [日付] と 2026-12-25 に公開
console.log(log.replace(/\d{4}-\d{2}-\d{2}/g, "[日付]"));
// [日付] と [日付] に公開replaceAll を使えば g を書かずに全部を置き換えられます。次の表のとおり使い分けます。
| 書き方 | 置き換える件数 | 備考 |
|---|---|---|
replace(文字列, ...) | 最初の 1 件 | |
replaceAll(文字列, ...) | すべて | |
replace(正規表現, ...) | g があればすべて。無ければ 1 件 | |
replaceAll(正規表現, ...) | すべて | g の無い正規表現を渡すとエラーになる |
最後の行が意外な点です。replaceAll に正規表現を渡すときは g が必須で、無いと TypeError で落ちます。
丸かっこで部分を取り出す
正規表現の中の丸かっこをキャプチャグループと呼びます。囲んだ部分は「当たった中身」として番号付きで取り出せて、置換文字列の中では $1 $2 の形で参照します。番号は左の丸かっこから数えて 1 始まりです。
const log = "2026-04-01 に公開";
console.log(log.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1年$2月$3日"));
// 2026年04月01日 に公開順番を入れ替えることもできます。
console.log("山田 太郎".replace(/(\S+)\s+(\S+)/, "$2 $1"));
// 太郎 山田$ を使った特別な書き方は次のとおりです。
| 書き方 | 意味 |
|---|---|
$1 $2 | 1 番目、2 番目のキャプチャグループ |
$& | 当たった全体 |
$$ | ドル記号そのもの |
$1が使えるのは置換文字列の中だけです。テンプレートリテラルの${...}とは別物なので、混ぜて書くと動きません。バッククォートで囲んでも$1は$1のまま扱われます。
置換関数で加工する
$1 は取り出した文字をそのまま差し込むだけです。加工はできません。先ほどの例で 04月 01日 と 0 が残ってしまったのは、そのためです。
加工したいときは、第 2 引数に文字列ではなく関数を渡します。当たるたびに呼ばれ、その戻り値が置き換え後の文字列になります。
const log = "2026-04-01 に公開";
const formatted = log.replace(/(\d{4})-(\d{2})-(\d{2})/g, (match, year, month, day) => {
return year + "年" + Number(month) + "月" + Number(day) + "日";
});
console.log(formatted);
// 2026年4月1日 に公開引数の並びは決まっています。
| 引数 | 中身 |
|---|---|
| 第 1 引数 | 当たった全体。$& と同じもの |
| 第 2 引数以降 | キャプチャグループが左から順に |
Number("04") は 4 になるので、これで先頭の 0 が落ちます。文字列の置換では書けなかった加工が、関数なら普通の JavaScript として書けるわけです。
置換関数は当たった件数だけ呼ばれます。
gを付けて 3 か所に当たれば 3 回呼ばれ、それぞれの戻り値がその場所に入ります。ループを自分で書く必要はありません。
キャプチャが要らないときは、第 1 引数だけ受け取れば足ります。
console.log("html css js".replace(/\w+/g, (match) => match.toUpperCase()));
// HTML CSS JS置換関数が効く場面
置換関数は、当たった文字を見てから何を返すか決められる点が強みです。表を引く形にすると、分岐を書かずに済みます。
const escapeMap = { "&": "&", "<": "<", ">": ">" };
function escapeHtml(text) {
return text.replace(/[&<>]/g, (ch) => escapeMap[ch]);
}
console.log(escapeHtml("5 < 10 & 20 > 10"));
// 5 < 10 & 20 > 10入門で innerHTML に文字を流し込むと、利用者の入力に含まれた < がタグとして解釈されてしまう話が出ました。上のような関数を通しておけば、文字として表示されます。当たった 1 文字をそのまま表のキーに使えるのが、置換関数の書き味です。
置換関数の中で
returnを書き忘れると、戻り値がundefinedになり、当たった箇所が文字列の"undefined"に置き換わります。エラーにならず、画面にundefinedが並ぶ形で表に出ます。
課題の考え方
学習ログの本文にある YYYY-MM-DD 形式の日付を、すべて YYYY年M月D日 に直します。月と日の先頭の 0 は落とします。手順は 3 つです。
/(\d{4})-(\d{2})-(\d{2})/gで、年と月と日をそれぞれ丸かっこで囲む- 第 2 引数に関数を渡し、
(match, year, month, day)を受け取る - 月と日は
Numberに通してから連結する
日付が 1 つも無ければ、replace は元の文字列をそのまま返します。分岐を書く必要はありません。
よくある間違い
戻り値を受け取らない
replace は元の文字列を書き換えません。呼びっぱなしにすると何も変わりません。
// NG 呼んだだけで捨てている
function formatLogDates(text) {
text.replace(/(\d{4})-(\d{2})-(\d{2})/g, "$1年$2月$3日");
return text;
}
// OK 戻り値を返す
function formatLogDates(text) {
return text.replace(/(\d{4})-(\d{2})-(\d{2})/g, "$1年$2月$3日");
}g を付け忘れて 1 件だけ置き換わる
テストケースが 1 件しか日付を含まないと気付けません。複数入った文字列で必ず確かめます。
const log = "2026-04-01 と 2026-12-25";
console.log(log.replace(/\d{4}-\d{2}-\d{2}/, "[日付]")); // [日付] と 2026-12-25
console.log(log.replace(/\d{4}-\d{2}-\d{2}/g, "[日付]")); // [日付] と [日付]置換関数の第 1 引数をキャプチャだと思う
第 1 引数は当たった全体で、キャプチャは第 2 引数からです。ここを 1 つずらすと、年のつもりで 2026-04-01 全体を受け取ってしまいます。
// NG year に "2026-04-01" が入る
log.replace(/(\d{4})-(\d{2})-(\d{2})/g, (year, month, day) => year);
// OK 先頭で全体を受け取る
log.replace(/(\d{4})-(\d{2})-(\d{2})/g, (match, year, month, day) => year);要件
- 関数名は formatLogDates、引数は文字列の text を 1 つ受け取る
- 4 桁の数字とハイフンで区切られた 2 桁の数字 2 つ、という形をすべて置き換える
- 月と日の先頭の 0 は落とす。04 は 4、01 は 1 にする
- 戻り値は置き換え後の文字列。日付が無ければ元の文字列をそのまま返す
入出力例
formatLogDates("2026-04-01 プロフィールサイトを公開") → "2026年4月1日 プロフィールサイトを公開"
formatLogDates("2026-04-01 と 2026-12-25 に公開") → "2026年4月1日 と 2026年12月25日 に公開"
formatLogDates("日付なしのメモ") → "日付なしのメモ"
formatLogDates("") → ""
formatLogDates("2026-4-1 は形が違う") → "2026-4-1 は形が違う"