preg系のPCRE関数で正規表現を使った置換

「PCRE」はモジュールのことで、このページで扱っている正規表現関数が使えます。他のモジュールでは「mbstring」や「POSIX」が用意されています。このうち「POSIX」はマルチバイト文字に対応していないため、PHPでは非推奨になっています。日本語を含むマルチバイト文字を処理する場合は、mbstring正規表現関数、もしくはUTF-8に文字エンコーティング変換したPCRE正規表現関数を使います。尚、PHPでは「PCRE」が標準で組み込まれています。

preg_replace()

preg_replace()の構文は「preg_replace('/パターン/', '置換後の文字列', 対象文字列 [, 希望する置換回数, [置換回数を返す変数]]);」です。
preg_replace()は正規表現で検索して置換します。検索パターンは文字列や配列です。パターン文字列では「|」を使ってのor指定やキャプチャによるサブパターン検索ができます。パターン配列では置換後の値を配列で置き換えることができます。

1
2
3
$string="データーベース01データーベースA";
// 第四引数以降を省略
echo preg_replace('/[\d+]{1,2}/', '03', $string);
実行結果:データーベース03データーベースA
1
2
3
4
5
$string="データーベース01データーベースA";
// キャプチャで保存して$1と$3だけ出力し、$2は非表示にして「ファイル」という単語に置換。
// また$3に対応する([0-9]+|[A-Z])では「|」のor演算子を使っており、
// 左右どちらか一方に該当すれば対応する方を$3として出力しています。
echo preg_replace('/(データー)(ベース)([0-9]+|[A-Z])/', '$1ファイル$3', $string);
実行結果:データーファイル01データーファイルA
1
2
3
4
5
6
$string="データーベース01データーベースA";
// 配列に対して配列で置き換えています。
$pattern=array('/[\d+]{1,2}/','/A/');
$replace=array('03','b');
echo preg_replace($pattern, $replace, $string, 1, $count);
echo '-置換回数'.$count;
実行結果:データーベース03データーベースb-置換回数2

preg_filter()

preg_filter()の構文は「preg_filter('/パターン/', '置換後の文字列', 対象文字列 [, 希望する置換回数, [置換回数を返す変数]]);」です。
preg_filter()は正規表現で検索して置換します。

1
2
3
$string="データーベース01";
// 第四引数以降を省略
echo preg_filter('/[\d+]{1,2}/', '05', $string);
実行結果:データーベース05
1
2
3
$string="データーベース01";
echo preg_filter('/[\d+]{1,2}/', '05', $string, 1, $count);
echo '-置換回数'.$count;
実行結果:データーベース05-置換回数1

preg_replace()とpreg_filter()の違い

preg_filter()では、変換されたものだけが返ってきます。preg_replace()では、変換されなかったものも返ってきます。

1
2
3
4
5
6
7
8
9
10
11
12
// 配列$stringに「#」という記号をわざと入れていますが、preg_filter()では返ってきません。
$string = array('1', 'a', '2', 'b', '#', '3', 'c', '4', 'd');
$pattern = array('/\d/', '/[a-z]/');
$replace = array('数字', 'ローマ字');
echo '
<div>preg_filter</div>
';
print_r(preg_filter($pattern, $replace, $string));
echo '
<div>preg_replace</div>
';
print_r(preg_replace($pattern, $replace, $string));

実行結果:

preg_filter

Array
(
[0] => 数字
[1] => ローマ字
[2] => 数字
[3] => ローマ字
[5] => 数字
[6] => ローマ字
[7] => 数字
[8] => ローマ字
)

preg_replace

Array
(
[0] => 数字
[1] => ローマ字
[2] => 数字
[3] => ローマ字
[4] => #
[5] => 数字
[6] => ローマ字
[7] => 数字
[8] => ローマ字
)

preg_replace_callback

preg_replace_callback()の構文は「preg_replace_callback(パターン, コールバック関数, 対象文字列 [, 置換制限, [置換回数]])」です。
preg_replace_callback()は正規表現検索を行い、コールバック関数で置換します。配列に対しては配列、それ以外は文字列を返します。preg_replace()の置換後の部分が、コールバック指定になるところと、preg_replace_callback()では複数パターンをまとめて記述できないところが、preg_replace()と異なる点です。

1
2
3
4
$a = 'PCREのpreg系による置換のためのマッチング機能';
// パターンの「preg」と「マッチング」が「preg_replace」に置き換えられます。
$pattern = '/preg|マッチング/';
echo preg_replace_callback($pattern, function(){return "preg_replace";}, $a);
実行結果:PCREのpreg_replace系による置換のためのpreg_replace機能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$a = '最大10歳と最大20歳と最大30歳';
// パターンキャプチャの「(最大)(\d+)(歳)」はそれぞれ、
// $matches[1]、$matches[2]、$matches[3]で取得できます。
// $matches[0]には、(最大\d+歳)の三つがまとめて入り、取得できます。
$pattern = '/(最大)(\d+)(歳)/';
echo preg_replace_callback(
$pattern,
function($matches){
print_r($matches);
$matches=$matches[2]+10;
// ここで最大を満、歳を才に置き換えています。
return '満'.$matches.'才';
},
$a
);
実行結果:Array
(
[0] => 最大10歳
[1] => 最大
[2] => 10
[3] => 歳
)
Array
(
[0] => 最大20歳
[1] => 最大
[2] => 20
[3] => 歳
)
Array
(
[0] => 最大30歳
[1] => 最大
[2] => 30
[3] => 歳
)
満20才と満30才と満40才

preg_replace_callback_array

preg_replace_callback_array()の構文は「preg_replace_callback_array(パターン(キー)とコールバック関数(値), 対象文字列 [, 置換制限, [置換回数]])」です。
preg_replace_callback_array()は正規表現検索を行い、コールバック関数で置換します。preg_replace_callback()と異なるところは、パターン単位で処理できる点です。preg_replace_callback()ではパターン文字列と対になる置き換え後の「|」(or)の処理がうまくできませんでした。そのため、配列に入ってきた「()」でキャプチャした値を非表示にするなどして面倒なコントロールをしなければならないようです。しかし、この関数では配列の中のキー(パターン)に対する値に直接無名関数を記述することで「|」(or)と同様の処理ができます。

1
2
3
4
5
6
7
8
9
10
11
$a = '最大10歳と最大20歳と最大30歳';
$b = preg_replace_callback_array(
[
// $aで出現する「最大」を$match[0]で三つ取得。returnで「満」に置換。
"/最大/" => function ($match) {return "満"; },
// $aで出現する「数字」を$match[0]で三つ取得。returnで取得した値に10を足して置換。
"/\d+/" => function ($match) { return $match[0]+10; },
// $aで出現する「歳」を$match[0]で三つ取得。$match[0]に「才を代入してreturnで返す。
"/歳/" => function ($match) {$match[0]="才"; return $match[0]; },
], $a);
print_r($b);

実行結果:満20才と満30才と満40才

preg_match()

preg_match()の構文は「preg_match('/パターン検索文字/', '対象文字列' [, 代入されるパターンマッチテキスト, [flags, [オフセット]]]);」です。
preg_match()は、正規表現を使ってマッチングを行います。第三引数以降は省略できます。また、第三引数には指定した変数(ここでは、$matches)にテキストが代入されます。$matches[0]にはパターン全体に一致した文字列が入り、$matches[1]にはサブパターンとしてキャプチャした一番目の文字列が入ります。それ以降は$matches[2]には二番目、$matches[3]には三番目にキャプチャした文字列という風に続きます。第四引数のflagsには、PREG_OFFSET_CAPTURE(各マッチに対応する文字列のオフセットも返されます。)が指定でき、この設定により$matchesを配列に変更します。第五引数のオフセットには検索開始位置のバイト単位を指定できます。その他、preg_match()は一致した場合に「1」を返し、一致しなかった場合には「0」を返します。エラー時には「false」を返します。

1
2
3
4
5
6
7
8
$strings="データベースAとデータファイルB";
// 「データファイルB」にはマッチしません。
preg_match('/(データ)(.*?)([A-Z])/', $strings, $matches);
echo '「'.$matches[0].'」';
echo '「'.$matches[1].'」';
echo '「'.$matches[2].'」';
echo '「'.$matches[3].'」';
print_r($matches);
実行結果:「データベースA」「データ」「ベース」「A」Array
(
[0] => データベースA
[1] => データ
[2] => ベース
[3] => A
)

preg_match_all()

preg_match_all()の構文は「preg_match_all('/パターン検索文字/', '対象文字列' [, 一致した全ての内容, [flags, [オフセット]]]);」です。
正規表現検索を繰り返し行います。第三引数以降は省略できますが、第三引数には第四引数のflagsで指定した形式の多次元配列が入ります。第四引数のflagsを設定しなければ、「PREG_PATTERN_ORDER」が指定されたことになります。
第四引数のflagsには、
PREG_PATTERN_ORDER($matches[0]にはパターンに一致した全ての配列、$matches[1]にはサブパターンとしてキャプチャした一番目の文字列が配列として入り、二番目以降は一番目に準じた形式で配列が作成されます。)、
PREG_SET_ORDER($matches[0]にはサブパターンとしてキャプチャした一番目の文字列、$matches[1]には二番目の文字列といった形式の配列が作られます。)、
PREG_OFFSET_CAPTURE(各マッチに対応する文字列のオフセットも返されます。値は配列の配列に変更されます。)、
が指定できます。
第五引数のオフセットには検索開始位置のバイト単位を指定できます。返り値は一致したパターンの総数ですが、エラー時には「false」を返します。

1
2
3
4
5
6
7
8
9
10
11
$strings="データベースAとデータファイルB";
// 「データファイルB」にもマッチします。
preg_match_all('/(データ)(.*?)([A-Z])/', $strings, $matches);
foreach($matches[0] as $val){
echo $val.'。';
}
echo '
<pre>';
print_r($matches);
echo '</pre>
';
実行結果:データベースA。データファイルB。

Array
(
    [0] => Array
        (
            [0] => データベースA
            [1] => データファイルB
        )

    [1] => Array
        (
            [0] => データ
            [1] => データ
        )

    [2] => Array
        (
            [0] => ベース
            [1] => ファイル
        )

    [3] => Array
        (
            [0] => A
            [1] => B
        )

)

preg_grep()

preg_grep()の構文は「preg_grep(パターン, 対象文字列 [, フラグ]);」です。
preg_grep()は配列のキーに対する値を返します。オプションの第三引数のフラグに「PREG_GREP_INVERT」を設定すると与えたパターンに一致しない要素を返します。

1
2
3
4
5
6
7
8
9
10
$a=array('a','b','c');
// 第一引数はキーに対する値、第二引数には配列を指定。
$b=preg_grep('/a/', $a);
echo '「'.$b[0].'」';
if(preg_grep('/a/', $a)){
echo '「aは存在します。」';
}else{
echo 'aはありません。';
}
print_r(preg_grep('/a/', $a, PREG_GREP_INVERT));
実行結果:「a」「aは存在します。」

preg_split()

preg_split()の構文は「preg_split(区切り文字のパターン, 対象文字列 [, 返却部分文字数, [flag]]);」です。
preg_split()は正規表現で区切り文字を指定して配列を作成します。第三引数と第四引数は省略できますが、第四引数のみ使いたい場合は、第三引数に「null」などを記述します。-1、0、NULLは制限が無いことを意味します。第三引数は部分文字列で最大何個を返すのかを指定します。第四引数には、PREG_SPLIT_NO_EMPTY(空文字以外を返す)、PREG_SPLIT_DELIM_CAPTURE(パターン中の カッコによるサブパターンでキャプチャした値も返す。)、PREG_SPLIT_OFFSET_CAPTURE(各マッチに対応する文字列のオフセットも返されます。)を指定します。尚、正規表現が必要でなければ、explode()やstr_split()のほうが高速です。

1
2
3
4
5
6
7
8
9
10
$str="jQuery&php,CSS#Perl HTML";
$array = preg_split("/[&,\s#]+/", $str, null, PREG_SPLIT_NO_EMPTY);
echo '
<div>';
print_r($array);
echo '</div>
';
foreach($array as $val){
echo $val.'。';
}
実行結果:

Array
(
[0] => jQuery
[1] => php
[2] => CSS
[3] => Perl
[4] => HTML
)

jQuery。php。CSS。Perl。HTML。

preg_quote()

preg_quote()の構文は「preg_quote(対象文字列 [, デリミタ]);」です。
preg_quote()は、正規表現文字をクオートします。特殊文字の前にバックスラッシュを挿入するため、実質的に特殊文字をエスケープします。 preg_replace()、preg_match()、preg_match_all()などと組み合わせて使われることが多いようですが、これらのでの利用を想定したものではありません。ただ、これといった問題もないようです。第二引数のデリミタには「/」が広く利用されているようです。

1
2
3
$strings="/css/style.css?ver=200";
$strings=preg_quote($strings, '/');
echo $strings;
実行結果:\/css\/style\.css\?ver\=200

preg_last_error()

preg_last_error()は正規表現処理で直近のエラーコードを返します。書式は、「preg_last_error ( void );」で、つまり、「preg_last_error ()」です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(preg_last_error() == PREG_NO_ERROR) {
echo 'エラーはありません。';
}
elseif(preg_last_error() == PREG_INTERNAL_ERROR) {
echo '内部エラーがあります。';
}
elseif(preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR) {
echo 'バックトラックの限界が尽きました';
}
elseif(preg_last_error() == PREG_RECURSION_LIMIT_ERROR) {
echo '再帰制限がなくなりました。';
}
elseif(preg_last_error() == PREG_BAD_UTF8_ERROR) {
echo '悪質なUTF8エラー';
}
elseif(preg_last_error() == PREG_BAD_UTF8_OFFSET_ERROR) {
echo '不正なUTF8オフセットエラー';
}
実行結果:エラーはありません。

PCRE関数で使える修飾子の例

「i」大文字小文字を区別しません。
「m」複数行あっても単一行として処理。通常、ループ処理などで正規表現の「^」や「$」とセットで使うことが多い。
「s」パターン中のドットメタ文字、改行を含む全ての文字にマッチさせます。複数行あっても一行にして処理します。
「x」空白を無視します。(エスケープしている場合や文字クラスの内部は除く。)
「A」検索対象文字列の先頭でのみマッチ。
「D」検索対象文字列の末尾でのみマッチ。
「S」追加パターンの分析が行われます。最初の文字が単一、固定でないパターンに対してのみ役立ちます。
「U」量指定子の「貪欲さ」が反転。
「u」パターンと対象文字列がUTF-8として処理されます。
「X」perl非互換の拡張機能を有効化。
「J」サブパターンで重複する名前を使う。