アイキャッチ画像: デスクの上でパソコンを使用している

本日は PHP でちょっとした文字列操作を行うときに欠かせない「 PHP の正規表現 」についてのまとめ記事を書いてみたいと思います。 今回は詳細まで事細かな網羅的な説明をするのではなく、使用頻度の高いものを中心に 重点ポイントに絞ったご説明 を目指したいと思います。

まず最初に本記事の前提について述べておきます。

  • 読者の方は正規表現を知っている
  • 読者の方は PHP を知っている

この前提のもと、正規表現そのものの詳しい説明や PHP の変数の型などの基本的な説明は割愛しています。 また、今回は 日本語のようなマルチバイト文字列の処理については対象外 とさせていただきます。 今回はご紹介しませんが、 PHP でマルチバイト文字列を扱うには以下にご紹介する preg_ 系の関数ではなく mb_ereg 系の関数を使う形になります。

正規表現とは

まずは正規表現について手短にご説明させていただきます。

正規表現とは、文字列の中から特定の「パターン」を検索するために使われる「 パターンを表すための表記法 」のことです。 Wikipedia の Regular Expression のページでは次のような説明がなされています。

a regular expression (abbreviated regex or regexp and sometimes called a rational expression[1][2]) is a sequence of characters that forms a search pattern, mainly for use in pattern matching with strings, or string matching, i.e. "find and replace"-like operations.

(意訳)正規表現( regex や regexp と呼ばれたりします。 rational expression と呼ばれることもあります)とは、検索パターンを表す文字の並びです。主に「検索と置換」のような処理のために文字列のパターンマッチングや文字マッチングに利用されます。

たとえば、「 Hello \S+ 」という正規表現は「 Hello world 」「 Hello Shiga 」「 Hello Umi 」などの文字列を表します。

正規表現の使いどころ

正規表現の使いどころとしては、文字列の中から特定の文字列を「完全一致」ではなく「 パターン一致 」で検索したいときに使います。 PHP の場合だと、ユーザがフォームに入力した値のバリデーション処理や、他のシステムと連携したときに渡される値を解釈するときに使ったりします。

逆に、文字列の中から特定の文字列を「完全一致」で検索したい場合にはあえて正規表現を使う必要はないでしょう。 パフォーマンスやコードのわかりやすさの観点から、完全一致検索の場合には通常の文字列処理用の関数( strpos() mb_strpos() strstr() など)を使うのがよいかと思います。

PHP で行う正規表現

PHP には正規表現を使うための関数が組み込みで多数用意されています。 関数名が preg_ ではじまる関数は大体正規表現絡みのもの です(余談ですが preg は「 Perform a REGular expression... 」の略だそうです)。 私が今ざっと確認したところでは以下のような関数が用意されていました。

  • preg_match()
  • preg_split()
  • preg_match_all()
  • preg_filter()
  • preg_replace()
  • preg_quote()
  • preg_last_error()
  • preg_replace_callback()

私自身これらの関数の中には使ったことがないものもありますし、「すべてを使ったことがある」という方はあまり多くはないのではないでしょうか。 それぞれの関数の特徴を手短に説明してみると次のような感じです。

  • preg_match(): 正規表現のパターンマッチを 1 回行う
  • preg_split(): 正規表現によって区切りパターンを指定して文字列を分割する
  • preg_match_all(): 正規表現のパターンマッチを可能なかぎり行う
  • preg_filter(): 正規表現にマッチしたパターンのみ(置換も行って)を返す
  • preg_replace(): 正規表現にマッチしたパターンを置換した文字列を返す
  • preg_quote(): 与えられた文字列を正規表現内で利用可能な形にエスケープして返す
  • preg_last_error(): 直前の正規表現処理のエラーコードを返す
  • preg_replace_callback(): 正規表現にマッチしたパターンを関数によって置換した文字列を返す

ここから、これらのうちでも一般的に最もよく使いそうな preg_match() preg_match_all() preg_filter() の 3 つについてもう少し詳しくご説明してみたいと思います。

preg_match()

preg_match() の宣言部は次のようになっています。

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

preg_match() は文字列の中に正規表現パターンにマッチする箇所があるかどうかを調べるための関数です。 あわせて、実際にマッチする箇所やそのインデックスを抽出する機能まで備えたたいへん使い回しの利く関数となっています。

具体的に使い方を見てみましょう。

<?php
print preg_match('/\S+d/', 'Hello world');  // => 1

print preg_match('/umi/', 'Hello world');  // => 0

パターンマッチがあるかどうかを調べたいだけであれば 2 つの引数を渡すだけで OK です。 第 1 引数に正規表現パターンを、第 2 引数に文字列を渡します。 マッチが発生したときには 1 を、発生しなかったときには 0 を、エラーの場合には FALSE を返してくれます。

マッチしたパターンを取得したい場合には第 3 引数を渡します。

<?php
print preg_match('/\S+d/', 'Hello world', $matches);  // => 1
var_dump($matches);  
// => array(1) {
//      [0] =>
//      string(5) "world"
//    }

print preg_match('/\umi/', 'Hello world', $matches);  // => 0
var_dump($matches);  
// => array(0) {
//    }

パターンがマッチした場合は第 3 引数に渡した変数の中にマッチパターンが配列で格納されます。

さらにマッチした文字列のインデックス(場所)を取得したい場合には第 4 引数を渡します。

<?php
print preg_match('/\S+d/', 'Hello world', $matches, PREG_OFFSET_CAPTURE);  // => 1
var_dump($matches);  
// => array(1) {
//      [0] =>
//      array(2) {
//        [0] =>
//        string(5) "world"
//        [1] =>
//        int(6)
//      }
//    }

print preg_match('/umi/', 'Hello world', $matches, PREG_OFFSET_CAPTURE);  // => 0
var_dump($matches);  
// => array(0) {
//    }

preg_match_all()

つづいて preg_match() の複数検索版 preg_match_all() について見てみましょう。 preg_match_all() の宣言部は次のようになっています。

int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )

preg_match_all() は文字列の中に正規表現パターンにマッチする箇所がいくつかるかを調べるための関数です。 こちらも preg_match() と同様、実際にマッチしたパターンを取得する機能も備えています。

preg_match() とほぼ同じ考え方で利用することができます。 具体的に使い方を見てみましょう。

<?php
print preg_match_all('/\S+/', 'Somewhere over the rainbow, Way up high');
// => 7

print preg_match_all('/blue/', 'Somewhere over the rainbow, Way up high');
// => 0

第 1 引数、第 2 引数については preg_match() と同様です。 マッチするパターンがあった場合にはその数を、なければ 0 を、エラーがある場合には FALSE を返します。

実際にマッチしたパターンを取得したい場合には第 3 引数を使いましょう。

<?php
preg_match_all('/\S+/', 'Somewhere over the rainbow, Way up high', $matches);
var_dump($matches);
// array(1) {
//   [0] =>
//   array(7) {
//     [0] =>
//     string(9) "Somewhere"
//     [1] =>
//     string(4) "over"
//     [2] =>
//     string(3) "the"
//     [3] =>
//     string(8) "rainbow,"
//     [4] =>
//     string(3) "Way"
//     [5] =>
//     string(2) "up"
//     [6] =>
//     string(4) "high"
//   }
// }

preg_filter()

もうひとつ、 preg_filter() という関数についてもざっと見てみます。 preg_filter() は検索パターンにマッチしたところを置換した上で返してくれる関数です。 宣言部は次のような感じになっています。

mixed preg_filter ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

具体的な使い方はこちら。

<?php
$pattern = '/Hello (\S+)/';
$replacement = '$0, $1';
$subject = 'Hello world';
var_dump(preg_filter($pattern, $replacement, $subject));
// => string(18) "Hello world, world"

第 1 引数には正規表現パターン、第 2 引数には置換パターンを渡します。 第 3 引数に対象となる文字列を渡します。 イメージとしては preg_match() で生成した $matches 変数の中身をさらに加工して返すという形でしょうか。

ちなみに、私はあまり使った経験がないので詳しくはわかりませんが preg_replace()preg_filter() とほぼ同じような関数のようです。

以上です。 いかがだったでしょうか?

今回は PHP で正規表現を使う方法をご紹介しました。 PHP には正規表現関係の関数が多数用意されていますが、使用頻度の高いものはそのうちのごく一部にかぎられているかと思います。 文字列をバリバリ処理しているシステムでもないかぎり正規表現を使いまわす機会というのは一般にはそう多くはないのではないでしょうし、どれもこれもまんべんなく押さえるのではなく、定番関数だけを重点的に押さえておくというのがよろしいかと思います。 通常の文字列操作ではなかなか難しい場合にサッと使えると便利な正規表現。 いざというときにささっと使えるようになりたいものですね。