正規表現で16本の記事を壊した。「どこまで取るか」を図にした
既定の指定は取れるだけ取ります。この性質で、実際にこのサイトの記事16本の本文を壊しました。何が起きたのかを図で説明します。

この記事の結論
- 既定の指定は取れるだけ取る。これが事故のもとになる
- 最小で止める指定を付けても、改行に合う指定を一緒に付けると区切りを越える
- 実際にこのサイトで16本の記事の本文を壊した
- 既定の指定は取れるだけ取る。これが事故のもとになる
- 最小で止める指定を付けても、改行に合う指定を一緒に付けると区切りを越える
- 実際にこのサイトで16本の記事の本文を壊した
このサイトの出典の書き方を直す置換を書いて、記事16本の本文を丸ごとリンクにしたことがある。原因は正規表現の「どこまで取るか」だった。

既定は「取れるだけ取る」
仕様の説明はこうなっている。
既定では
*や+のような量指定子は貪欲(greedy)であり、できるだけ多くの回数に合わせようとする。
そして、最小で止めるほうはこう書かれている。
量指定子のあとに
?を付けると非貪欲になる。つまり、最小の回数が見つかった時点で止まる。
公式の例が分かりやすい
対象が some <foo> <bar> new </bar> </foo> thing のとき、結果はこうなる。
| 書き方 | 合う範囲 |
|---|---|
<.*> | <foo> <bar> new </bar> </foo>(最後まで一息に取る) |
<.*?> | <foo>(最初で止まる) |
書いたのは同じ1文字の違いで、取れる範囲がこれだけ変わる。
実際に壊したときの話
出典の一覧で、リンクの文字をURLそのものから名称に変えたかった。書いたのは、おおよそこういう形だった。
<li>(.*?)<a href=…に、改行にも合う指定を付けたもの
最小で止める指定は付けている。それでも足りなかった。
なぜ越えたか
最小で止めるのは「次に続きが見つかるところまで」であって、「区切りの手前まで」ではない。
1つ目の項目の中に続きが無ければ、区切りを越えて次の項目まで探しに行く。改行に合う指定を付けたことで、行をまたぐことも妨げられなかった。
結果、項目と項目の間にあった本文がまるごとリンクの中に入った。記事を開くと、本文全体が青い下線になっていた。16本すべてで同じことが起きた。
直し方と、入れた検査
直した形は単純で、一息に書くのをやめた。
- まず
<li>…</li>を1件ずつ取り出す - 取り出したその中だけを置換する
- 置換後にリンクの中にブロック要素が入っていないかを調べる
- 入っていたら書き戻さずに中止する
3番目と4番目が要点になる。壊れたことを、公開する前に機械で気づける形にする。見た目で確認する運用にすると、16本あれば必ず見落とす。
決めたこと
この一件から、置換を書くときの決まりを2つ置いた。
- 本文をまたげる書き方をしない。区切りで先に切ってから中を処理する
- 書いてしまったら、必ず検査を付ける。検査が無い置換は流さない
以後の置換は、先読みで区切る形だけを使っている。サーバー側の検査と同じ考え方で、信用せずに確かめる側を用意する。
覚えておく書き方
| 書き方 | 意味 | 気をつけること |
|---|---|---|
. | 任意の1文字 | 既定では改行に合わない |
* + | 0回以上/1回以上 | 既定で取れるだけ取る |
? | 0回か1回 | 量指定子の後ろに付けると最小で止まる |
[ ] | この中のどれか1文字 | [^ ] は「この中以外」 |
( ) | まとめて覚える | 後から取り出せる |
^ $ | 先頭/末尾 | 行ごとにする指定で挙動が変わる |
1行目が効いてくる。改行に合わないのが既定だからこそ、改行に合う指定を足した瞬間に範囲が広がる。
行単位で絞る話はgrepは行ごとに見ているだけに書いた。あちらは行をまたがないことが守りになっている例になる。
まとめ
- 既定の量指定子はできるだけ多くの回数に合わせようとする
- 後ろに
?を付けると最小の回数で止まる - 最小で止めても、区切りの手前で止まるわけではない
- 改行に合う指定を足すと行をまたぐ。これで16本を壊した
- 直し方は区切りで先に切り出してから、中だけ処理する
- 置換には壊れを機械で見つける検査を必ず付ける
出典
- MDN Web Docs「量指定子」(貪欲・非貪欲の定義と、
<.*>と<.*?>の例。2026年10月13日に確認)
図は本記事のために作成したもの。壊した件は、このサイトで2026年10月に実際に起きたもの。
新しい記事が出たら、ブラウザの通知でお知らせします。
メールアドレスの登録は要りません。いつでも止められます。
関連記事
RELATED
grepは行ごとに見ているだけ。終わったときの番号でつまずいた
grepが何をしているのかを図にしました。見つからなかったときに返る番号が1で、手順の中で使うと止まります。
0.1 + 0.2 が 0.3 にならない理由を図にした
壊れているのではなく、10進で割り切れる数が2進では割り切れないためです。比較の仕方と、金額を扱うときの原則を整理しまし…
REST APIの考え方を図にした。URLに動詞を入れない
RESTは制約条件の集まりです。URLは物を指し、動作はメソッドで表します。RESTfulと呼ばれるAPIが必ずしも準拠…