コンテンツへスキップ

正規表現入門

正規表現とは何か、何に使えるのか、そしてどう使い始めるか――実践的な概要をお届けします。

正規表現入門

そもそも正規表現とは?

正規表現(略してRegex)は、他の文字列に対してよくある操作を行うためのDSL(ドメイン固有言語)として機能する文字列です。DSLは「プログラミング言語の中のプログラミング言語」とも言えます。

Regexの場合、外側のプログラミング言語はString型をサポートしている任意の言語で、Regexをサポートしていれば何でも構いません。ほぼすべての主要なプログラミング言語がRegexをサポートしているため、Regexを知っておくことは非常に有用です。Regexの内部言語はStringだけで構成されており、一部の文字に特別な意味があります。

例えば、文字列".*@.*\\.com"の中で、.は「任意の1文字」、*は「直前の要素が任意の回数」を意味します。.*を合わせると「任意の文字が任意の回数」という意味になります。次に特別な意味を持たない文字@があり、続いて再び.*、そして\\は「次の文字をエスケープして特別な意味を持たない通常の文字として扱う」という意味なので、\\.は「任意の文字」という特別な意味ではなく、通常の.文字として読みます。最後にcomは特別な意味を持たない単なる文字の並びです。全体として、このRegexは.comで終わりかつどこかに@を含むメールアドレスの簡易的なマッチャーになっています。

「正規表現DSL」で何ができるの?

ℹ️ Rubyがインストールされていれば(Macにはプリインストールされています)、irbと入力してinteractive Rubyシェルを起動し、以下のサンプルを試すことができます。

Regex文字列と組み合わせて使える主な機能は3つあります。

  1. matches: Regexと別の文字列を受け取り、その文字列がRegexに「マッチ」するかどうかを判定します。指定されたRegexに一致する部分が文字列内に「ある」場合はtrue、なければfalseを返します。Rubyでの例(matchesmatch?と呼ばれます ―― ?は関数名の一部です):

/.*@.*\\.com/.match?('[email protected]') # => false /.*@.*\\.com/.match?('[email protected]') # => true
  1. captures: Regexと別の文字列を受け取り、Regexのマークされた部分に一致する部分文字列を読み取ります。Regex内の部分は()でマークでき、「キャプチャグループ」と呼ばれます。Rubyでの例(capturesMatchオブジェクトからアクセスできます):

/(.*)@(.*)\\.com/.match('[email protected]').captures # => ["queenie.goldstein", "ilvermorny"]
  1. replace: Regexとテンプレート文字列を受け取り、マッチした部分を指定された文字列で自動的に置換します。キャプチャグループは$1$2や、言語によっては\\1\\2などで参照できます。Rubyでの例(replacegsubと呼ばれます):

'[email protected]'.gsub(/(.*)@(.*)\\.com/, '\\1@\\2.org') # => "[email protected]"

「正規表現DSL」はどんな見た目?

これについては、素晴らしい例付きの「チートシート」がたくさんあります。

大きく分けて、理解すべきDSLコンポーネントは5種類あります。

1. 文字/グループ修飾子(例:*+{,}?

Regexの基本的な「構成要素」は文字です。各文字の後に、直前の文字が何回マッチするかを指定する修飾子を書くことができます。利用可能な修飾子は以下の通りです。

0回または1回: ?(例:a?b?c?aababcbccすべてにマッチ)

ちょうど1回: 修飾子なし(デフォルト)

0回から♾️回: *(例:a*bcbcabcaaabcにマッチ)

1回から♾️回: +(例:a+bcabcaaabcにマッチするがbcにはマッチしない)

ちょうどX回: {X}(例:a{3}bcaaabcにマッチするがaabcaaaabcにはマッチしない)

X回からY回: {X,Y}(例:a{2,5}bcaaaaabcにマッチするがabcにはマッチしない)

X回から♾️回: {X,}(例:a{2,}bcaaaaaaaabcにマッチするがabcにはマッチしない)

同じ修飾子はグループにも使えます(例:(abc)+)(グループについては後述)。

カスタムセット([]で作成)

文字のカスタムセットは、括弧内に区切り文字なしで列挙して定義できます。例えば、a、b、cと数字1、2、3のセットは[abc123]と書きます。これは「このセットの中の1文字」として扱われるため、複数マッチするには[abc123]*[abc123]{2,5}のように文字修飾子が必要です。

カスタムセットの先頭に^を使うと、括弧内で指定した文字以外の任意の文字を受け入れることもできます。例えば[^\\n]は改行以外の任意の文字を受け入れます。

連続している文字(数字やアルファベットなど)は、-を間に入れて範囲指定もできます。例:[a-zA-Z0-9]

[abc123]{3,}abcabにはマッチしませんが、111abcにはマッチします。

定義済みセット(\\s\\S\\d\\D\\w\\W

以下のセット(簡略化)はあらかじめ定義されており、直接使用できます。

  • \\s ― 実質的に[ \\t\\n]と同じ、「任意の空白文字」と読みます

  • \\S ― 実質的に[^ \\t\\n]と同じ、「空白以外の任意の文字」と読みます

  • \\d ― 実質的に[0-9]と同じ、「任意の数字」と読みます

  • \\D ― 実質的に[^0-9]と同じ、「数字以外の任意の文字」と読みます

  • \\w[a-zA-Z_0-9]に近い(ウムラウトなども含む)、「任意の単語文字」と読みます

  • \\W[^a-zA-Z_0-9]に近い、「単語文字以外の任意の文字」と読みます

グループ(()(?<name>)

グループは「単語」や「文」のようなものと考えることができます。修飾子のデフォルトの構成要素を「文字」から文字のセット(つまり「グループ」)に変更します。例えば、abc*は「aが1回、bが1回、cが任意の回数」と読みますが、「abcが任意の回数」と書きたい場合は(abc)*とします。abcが1つのグループとして扱われ、abcabcabcという文字列全体にマッチします。

グループでは、選択肢を指定することもできます。|で異なる選択肢を区切ったグループを書きます:(abc|def) ―― これは「abcまたはdef」と読み、123abc123456def456の両方にマッチしますが、adbecfにはマッチしません。

キャプチャグループはRegexの一部を取り出し、番号や名前を割り当てます。コードや置換テンプレート文字列で参照するために使います。(.*)@(.*).comのようなキャプチャグループは、\\1@\\2.com$1@$2.com(言語によります)で参照できます。

グループに名前を付けることもできます。例えば(?<user>.*)@(?<domain>.*).comとすれば、${user}@${domain}.comのように参照できます。ただし、これは高度な機能であり、言語によって実装が異なります(一部の言語ではサポートされていません)。

マッチ修飾子(\\A\\z^$、先読みと後読み)

デフォルトでは、abcのようなRegexのマッチはcontainsメソッドのように動作します。しかし、abcが文字列の先頭や末尾、または行の先頭や末尾にある必要があると指定することもできます。例えば、^abc^は新しい行の先頭にabcがある文字列のみマッチします。def\\nabcにはマッチしますが、defabcにはマッチしません。abc$$abcの後に行末があることを確認します。文字列全体(複数行を跨いで)でマッチするには\\A\\zを使います。

先読みと後読みはより高度なトピックで、Regexの先頭や末尾が特定のRegexにマッチ「しない」ことを確認したい場合に主に役立ちます。ほとんどの場合、先読みや後読みを使ったRegexはキャプチャグループで書き直せるので、まずキャプチャグループで書くことを試み、他の方法がうまくいかない場合にのみ先読み・後読みについて調べてください。先読み・後読みはCPU負荷が高い操作であり、制限もあります(例えば、ほとんどの修飾子をサポートしていません)。

先読み・後読みについて学ぶには、以下が良い情報源です。

よくある注意点と新しいRegexの検証

よくある注意点として、ほとんどの言語では.はデフォルトで改行文字にマッチしません。ただし、オプションでオンにできることが多く、Rubyでは末尾に/mを指定することで「ドットが改行にもマッチする」ようになります。

また、言語ごとに文字列の仕組みにより予約されている文字が異なることにも注意してください。例えば、Rubyでは/\\/でエスケープする必要がありますが、Swiftではそのエスケープは不要な代わりに{}\\{\\}でエスケープする必要があります。他の言語向けに書かれたRegexをコピー&ペーストする際には、この違いを覚えておくことが重要です。

一般的に、新しいRegexを書く際には、以下の3つの機能を持つWebサイトやツールの使用をお勧めします。

  1. マッチ対象のサンプル文字列を追加できるオプション

  2. Regexチートシートが画面上に表示されていてすぐに参照できること

  3. 入力したサンプル文字列に対してRegexがリアルタイムでマッチする表示

私が使っているサイトはこちらです(バックグラウンドでRubyが動いています)。

Rubular

今日からプロジェクトでRegexを使うには?

Regexを使う良い機会を待つ必要はありません。AnyLintを使えば、正規表現でプロジェクトをリントできます(自動修正もサポート)。

github.comFlineDev / AnyLintLint anything by combining the power of scripts & regular expressions

この記事が参考になりましたか?BlueskyMastodonでフォローして、Swiftのヒントやインディー開発の最新情報をチェックしてください。