正規表現について

rikuo kamadarikuo kamada

1. はじめに(レビュー後・修正版)

正規表現を学習する機会があったので、ここに備忘録としてまとめておくことにします。

正規表現とは、効率的に特定の文字列の置換・検索・抽出など「数字が連続する部分」や「特定の形式を満たす文字列」といった構造的な条件を扱う処理の際に使われる表記方法のことです。

WordやvsCodeなどの多くのエディタでは ctrl + F や ctrl + H で検索や置換を行いますが、ターミナルやプログラム内で使うにはそうはいきません。

適切な命令をCLIやプログラミング言語での記述によって出し、結果を取得する必要があります。このときに使われるのが正規表現です。

本記事では、正規表現の基礎的な考え方から実務で注意すべきポイント、少し踏み込んだ話題までを順に整理していきます。

2. 最小限で使える基礎構文セット

・直接的な文字の一致

abc

最も基本的な正規表現です。この例だと、”abc” という文字列がそのまま含まれている箇所に一致します。

これに関しては、ただの文字検索とほぼ変わりません。

さらに実際に使う場合では言語によって

const patternCheck = /abc/.test("kfabccc")
grep -rl "abc" ./

のように「/」を使ったり使わなかったりし、記述が少し異なる場合もあります。

このような言語によって正規表現の書き方が一部違う話は後で書くことにします。

・特殊文字(メタ文字)

正規表現では、一部の文字が特別な意味を持ちます。これらは一般に「特殊文字」や「メタ文字」と呼ばれます。

.

任意の1文字に一致します(改行を除く場合が多い)。

例:

  • a.cabca1c には一致するが、ac には一致しない

1文字だけを抽象化したいときに使われますが、意図しないマッチが起こることがよくあり注意が必要です。


a c   ← マッチする
a-c   ← マッチする
a1c   ← マッチする
a c  ←(全角スペース)← これもマッチする場合がある

^

行(または文字列)の先頭を表します。

  • ^abcabc123 には一致するが、123abc には一致しない

$

行(または文字列)の末尾を表します。

  • abc$123abc には一致するが、abc123 には一致しない

*

直前の文字やグループが 0回以上 繰り返されることを意味します。

  • ab*cac, abc, abbc に一致する(この例だと b が0回以上)

+

直前の文字やグループが 1回以上 繰り返されることを意味します。

  • ab+cabc, abbc に一致するが、ac には一致しない

?

直前の文字やグループが 0回または1回 出現することを意味します。

  • ab?cac, abc に一致する

これらの記号は単体で意味を持つというよりも、直前の要素に対してどのような条件を与えるかを指定するために使われます。

・量指定子 {}

{} は、直前の文字やグループが何回出現するかを明示的に指定するための記法です。

  • {n} : ちょうど n 回
  • {n,} : n 回以上
  • {n,m} : n 回以上 m 回以下

例:

  • a{3}aaa に一致する
  • a{2,}aa, aaa, aaaa に一致する
  • a{2,4}aa, aaa, aaaa に一致するが aaaaaa には一致しない

*+? はよく使われますが、実体としては {} の省略記法です。回数条件を正確に制御したい場合には {} を使う方が意図が明確になります。

+ * ? {} のみだと基本的に直前の一文字しか指定できないため、()でグループ化することでまとまりで指定することができます。

・グループ化 ()

() は、正規表現の一部をひとまとまりとして扱うための記法です。これにより、量指定や後続処理の対象をまとめることができます。

例:

  • (ab)+ab, abab, ababab に一致する
  • ab+ab, abb, abbb に一致する(意味が異なる)

このように、() を付けるかどうかで 量指定の対象が変わる ため、正規表現の意味が大きく変化します。

また、

(one | two | three)

のように特定の単語を指定することもできます。

また、() で囲まれた部分は キャプチャ として扱われ、検索結果や置換時に参照できる場合があります。この点については後の章で詳しく扱います。

・文字クラス [ ]

[ ] は、指定した文字のいずれか1文字に一致させるための記法です。. のように「何でも1文字」ではなく、許可する文字の集合を明示できる点が特徴です。

例:

[a-c]
[abc]

a または b または c のいずれか1文字に一致します。

[a-z]

小文字アルファベット1文字に一致します。

[a-zA-Z]

大文字・小文字を区別せず英字1文字に一致します。

. とは異なり、空白や記号などを意図せず拾わないため、1文字を限定したい場合はこちらを使う方が安全です。

・否定文字クラス [^ ]

[^ ] は、指定した文字以外の1文字に一致させるための記法です。^[ の直後にある場合は、先頭アンカーではなく「否定」の意味になります。

例:

[^0-9]

数字以外の1文字に一致します。

[^ ]

空白以外の1文字に一致します。

[^a-z]

小文字アルファベット以外の1文字に一致します。

このように、否定文字クラスを使うと「除外条件」を簡潔に書くことができます。

・\ に続く特殊文字(エスケープシーケンス)

\ に続く特定の文字があらかじめ意味を持つ記号として定義されている物があります。これらは文字クラスと並んで非常によく使われます。

ここでは、特に使用頻度の高いものだけを整理します。

\d

  • 数字1文字に一致します
  • [0-9] とほぼ同じ意味です

\D

  • 数字以外の1文字に一致します
  • [^0-9] とほぼ同じ意味です

\w

  • 英数字とアンダースコア(a-zA-Z0-9_)のいずれか1文字に一致します

\W

  • \w 以外の1文字に一致します

\s

  • 空白文字に一致します
  • スペース、タブ、改行などが含まれます

\S

  • 空白文字以外の1文字に一致します

\A

  • 文字列の先頭に一致します
  • ^ と似ていますが、行単位ではなく文字列全体の先頭を基準にします

\z

  • 文字列の末尾に一致します
  • $ と似ていますが、改行の影響を受けません

これらの記号は、記述がシンプルになるという利点がありますが、正規表現エンジンや言語によって異なる場合があり注意が必要です。

・半角英数字を想定したバリデーション例

ここまでの要素を組み合わせた例として、次のような正規表現を見てみます。

^[a-z0-9]+(?:-[a-z0-9]+)*$

この正規表現は、半角英小文字と数字を基本とし、途中にハイフンを含む形式を想定したものです。URL の一部(slug)や識別子などでよく使われます。

全体構造を見ると、次のように分解できます。

  • ^ : 文字列の先頭
  • $ : 文字列の末尾

これにより、「途中に含まれている」ではなく、文字列全体がこの形式であることを条件にしています。

[a-z0-9]+
  • [a-z0-9] : 半角英小文字または数字
  • + : 1文字以上

まず、英小文字または数字が1文字以上続くことを表しています。先頭がハイフンになることはありません。

(?:-[a-z0-9]+)*
  • - : ハイフンそのもの
  • [a-z0-9]+ : 英小文字または数字が1文字以上
  • (?: ) : グループ化するがキャプチャはしない(あくまでバリデーションなのでキャプチャの必要はない)
  • * : このまとまりが0回以上繰り返される

この部分により、

  • abc
  • abc-123
  • abc-123-def

のような形式を許可します。一方で、

  • -abc
  • abc-
  • abc--def

といった形式は弾かれます。

このように、

  • 文字クラス
  • 量指定
  • グループ化
  • アンカー

を組み合わせることで、「使われそうな形式だけを許可する」バリデーション用の正規表現を書くことができます。

3. その他の正規表現で扱う概念について

ここからは、基礎構文を一通り押さえた上で知っておくと理解が深まる概念についてまとめていきます。

・キャプチャ(capture)

正規表現におけるキャプチャとは、() で囲まれた部分に実際に一致した文字列を「記録しておく」仕組みのことです。

単に一致するかどうかを判定するだけでなく、「どの部分がどんな文字列に一致したのか」を後から参照できるようになります。

例えば、次のような正規表現があるとします。

(\d{4})-(\d{2})-(\d{2})

これを 2026-01-19 という文字列に対して使うと、

  • 全体としては 2026-01-19 に一致する
  • 1つ目の () には 2026
  • 2つ目の () には 01
  • 3つ目の () には 19

というように、それぞれのグループに一致した内容が保存されます。

この「保存される部分」がキャプチャです。

キャプチャされた値は、

  • 検索結果から特定の部分だけを取り出す
  • 置換時に元の文字列の一部を再利用する

といった用途で使われます。

日付やID、ログの分解などではよく登場します。

・グループ化とキャプチャの違い

() は見た目は一つですが、実際には

  • グループ化(まとまりとして扱う)
  • キャプチャ(一致した内容を記録する)

という2つの役割を同時に持っています。

そのため、「量指定のためにまとめたいだけ」の場合でも、

() を使うと自動的にキャプチャが発生します。

この挙動が分かりにくさの原因になることがあります。

・非キャプチャグループ (?: )

キャプチャが不要な場合には、(?: ) という記法を使うことができます。

これは、

  • グループ化はする
  • 一致した内容は記録しない

という指定です。

例えば、先ほど例に出した正規表現、

^[a-z0-9]+(?:-[a-z0-9]+)*$

では、途中の (?:-[a-z0-9]+) をキャプチャせず、あくまで構造を表現するためだけにグループ化しています。

バリデーションのように「通るか・通らないか」だけが重要な場面では、このような非キャプチャグループがよく使われます。

キャプチャは便利な仕組みですが、必要以上に増やすと正規表現が読みにくくなるため、

「取り出したいときだけ使う」という意識を持つと整理しやすくなります。

参考:

crapp.hatenablog.com
https://crapp.hatenablog.com/entry/2015/02/12/220539

Author

rikuo kamadarikuo kamada

主にシステム面で学習したことをまとめています。 フロント、サーバー、インフラ、AIなど細かい分野に絞らずに広く発信していきます。

正規表現について | KMD WORKS