🐧 Linux 総合学習プラットフォーム
正規表現・テキスト処理 ・ 中級

量指定子で繰り返しを表す

直前のパターンが何回繰り返されるかを表すのが「量指定子」です。* は0回以上、+ は1回以上、? は0回または1回(あってもなくてもよい)を意味します。回数を細かく指定したいときは {n} でちょうどn回、{n,} でn回以上、{n,m} でn〜m回と書けます。例えば ab* は a の後に b が0個以上、go+gle は o が1個以上、colou?r は color と colour の両方にマッチします。+ ? { } は基本の正規表現では \ が必要になる点に注意します。

ここまでで、1文字を指す道具(. や文字クラス)を学んだ。しかし実際のパターンを書こうとすると、すぐ壁にぶつかる。「直前の文字が何回くり返されるか」を言いたい場面が、ほとんどなのだ。

数字が何桁続くか分からない、空白が1個か複数か分からない、ある文字があってもなくてもよい。こうした「回数」を表すのが量指定子だ。

💡
ポイント量指定子は「直前のパターンを何回くり返すか」を指す道具。1文字を指す道具と組み合わせて、正規表現の表現力を一気に広げる。

量指定子は単独では使えない。必ず直前のパターン(1文字や文字クラス、グループ)に対して「それを何回くり返すか」を指定する形で働く。

1文字を指す道具と回数を指す道具を組み合わせることで、正規表現は一気に表現力を増す。回数を表す記号から見ていこう。

🔢 0以上・1以上・0か1――この差を取り違えると、何が起きるか

もっとも基本的な量指定子は * + ? の3つだ。

*0回以上あってもなくても・何個でも+1回以上最低1個・何個でも?0回か1回多くても1個ab* → a,ab,abb / go+gle → gogle,google / colou?r → color,colour* + ? は順に {0,} {1,} {0,1} と同じ意味

* は0回以上のくり返しを表し、「あってもなくてもよく、何個でもよい」という最も緩い指定だ。+ は1回以上、つまり「最低1個は必要で、何個でもよい」を表す。? は0回または1回、すなわち「あってもなくてもよいが、多くても1個」を意味する。

違いを具体例で見よう。ab* は a の後ろに b が0個以上なので a, ab, abb… に一致する。

go+gle は o が1個以上必要なので gogle, google, gooogle に一致するが、o が無い ggle には当たらない。

colou?r は u が0個か1個なので、color と colour の両方にちょうど一致する。

この3つの違い(0以上・1以上・0か1)を取り違えると、拾いたいものを取りこぼしたり、余計なものを拾ったりする。最初に丁寧に区別しておこう。

🎚 ちょうど何回、と厳密に言いたいときは

* + ? は便利だが、「ちょうど4回」「2回以上」といった厳密な回数は表せない。「ちょうど何回」「何回以上」「何回から何回まで」と指定したいときは、波かっこ { } を使う。

{n} はちょうどn回、{n,} はn回以上、{n,m} はn回からm回までを表す。

[0-9]{4} は「数字がちょうど4個」。echo '西暦2026年と12月' から拾うと、一致するのは2026だけで、2桁の12は当たらない。

たとえば [0-9]{4} は「数字がちょうど4個」で、西暦の年号やコードの桁数チェックに使える。echo '西暦2026年と12月' から4桁を拾う例なら、一致するのは2026だけで、2桁の12は当たらない。

[0-9]{2,4} なら2個から4個、[0-9]{2,} なら2個以上だ。a{3} は aaa にちょうど一致する。回数が決まっている入力の検証では、この波かっこが正確で読みやすい武器になる。

じつは * + ? は波かっこで言い換えることもできる。* は {0,}、+ は {1,}、? は {0,1} と同じ意味だ。

こう並べてみると、3つの基本記号がそれぞれ「回数のよくある型」に名前を付けたものだと分かり、量指定子全体を一つの考え方として整理できる。

🌗 同じ式なのに、なぜ環境で当たったり当たらなかったりするのか

ここで一つ、つまずきやすい落とし穴がある。+ ? { } は、grep や sed が既定で使う基本正規表現(BRE)では、そのままだと特別な意味を持たず、ただの文字として扱われるのだ。

BRE でこれらを量指定子として使うには、直前にバックスラッシュを付けて \+ \? \{ \} と書く必要がある。

つまずきBRE では + ? { } はそのままだとただの文字。量指定子にするには \+ \? \{ \} と書く。ERE(grep -E)ならエスケープなしで使える。

たとえば BRE で「a が1回以上」を表すには echo 'aaa' | grep -o 'a\+' と書く。grep -o 'a+' と書くと + はただの文字になって「a に続くプラス記号」を探してしまい、何も一致しない。

一方、後の項目で扱う拡張正規表現(grep -E)では、これらをエスケープなしで a+ や a{2,4} と書ける。

* . ^ $ [ ] の基本記号はどちらの方言でも同じなので、まずはそこを共通の土台と考えてよいだろう。「同じ式が動かない」と感じたら、いま自分がどちらの方言にいるかを疑うのが先決だ。

🍴 sedの置換で、なぜ思ったより広く消えるのか

もう一つ知っておきたいのが、* や + は既定で「できるだけ長く」一致しようとする性質だ。これを貪欲(greedy)マッチと呼ぶ。

<a>X</a><b>Y</b><.*> → 最初の < から最後の > まで丸ごと一致(貪欲)<[^>]*>→ > 以外をくり返し、次の区切りで止まる「区切り文字以外をくり返す」で狙った範囲で止める

たとえば <a>X</a><b>Y</b> という文字列に <.*> を当てると、最初の < から最後の > まで丸ごと一致してしまい、タグ1個だけをうまく取り出せない。

狙った範囲で止めたいときは、<[^>]*> のように「> 以外をくり返す」と書いて、次の区切りで自然に止まるようにするのが定番のテクニックだ。

コツ貪欲マッチで広く一致しすぎるなら、<[^>]*> のように「区切り文字以外をくり返す」と書く。次の区切りで自然に止まる。

この「区切り文字以外をくり返す」という発想は、カンマ区切りの1項目を取るときの [^,]* など、さまざまな場面で応用が効く。

なお PCRE(grep -P)が使える環境なら .*? という非貪欲指定で「できるだけ短く」一致させることもできるが、移植性は落ちる。

sed の置換で「思ったより広い範囲が消えた・置き換わった」というときは、まずこの貪欲マッチを疑うと原因にたどり着ける。

⚠ つまずいたら、まずこれ

失敗として多いのは、+ と * の混同だ。たとえば空白で区切られた値を扱うとき、区切りを ' *'(空白0個以上)と書くと空白が無くても一致してしまい、' +'(空白1個以上)と意図が変わる。

また BRE と ERE で \ の要否が逆になることを忘れ、grep 'a{2}' のように書いて当たらない、というのも頻出のつまずきだ。

もう一つ見落としやすいのが、量指定子は「直前の1つ」にしか効かないという原則だ。abc+ は「abc のくり返し」ではなく「ab に続いて c が1個以上」を意味し、まとまり全体をくり返したいなら (abc)+ のようにグループ化が要る。

つまずき量指定子は「直前の1つ」にしか効かない。abc+ は「abc のくり返し」でなく「ab に続いて c が1個以上」。まとまりをくり返すには (abc)+ が要る。

実務では、量指定子は入力の形式検証(年号やコードの桁数チェック)、ログからの抽出(連続する数字や英字の並びを取る)、空白の正規化(連続する空白を1つにまとめる置換)など、至るところで使う正規表現の心臓部だ。

たとえば「行末の余分な空白を消す」なら sed -E 's/ +$//'、「連続する空白を1つにまとめる」なら sed -E 's/ +/ /g' のように、量指定子が置換の対象を柔軟に表現してくれる。

「* は0以上、+ は1以上、? は0か1、{n,m} で回数指定、BRE では \ が要る」。この対応を押さえておけば、狙ったくり返しを過不足なく書けるようになる。

この項目に出てくる用語

量指定子りょうしていし
直前のパターンの繰り返し回数を表す記号。* + ? {n} など。

関連コマンド

grep

▶ 学習アプリでこの続きを学ぶ・演習する