PHP 編
preg 系関数と ereg 系関数を学ぶ
Shibuya Perl Mongers
小山 浩之 OYAMA Hiroyuki

oyama@cpan.org

http://module.jp/

定・キャプチャなどなど，第1 章で解説した一般的なはじめに正規表現の利用方法は変わりません．しかし利用できるメタ文字や機能や制限事項についてはそれぞれ異な

現在PHPではpreg系関数，ereg系関数，mb̲eregっているので注意が必要です．特にマルチバイト対応系関数という，それぞれ実装の異なる3 種類の正規表のmb̲ereg 系関数は現在実験的に導入されている状

現ライブラリが利用できます．本章ではPerl 5.005 互態ですので，将来的にAPIや機能が変更・削除される換の正規表現を実装するPCRE ライブラリをベースと可能性もありこちらも利用に際して注意が必要です．

したpreg系関数を中心に，IEEE 1003.2ベースのereg

現状では特に理由がない限り性能面，機能面，仕系関数との違いやその利用方法を解説します．

様の安定性，異なるプラットフォームでの互換性などから見てpreg系関数を利用するのが無難な状態ですの

PHP の方言で，本章ではpreg系関数を中心に解説します．

マッチング

PHPで利用できる正規表現にはPerlバージョン5.005の正規表現の機能を再現したPCRE（Perl-compatible

変数$text に格納した文字列にpreg 系関数でマッチ

regular expression library）ベースのpreg 系関数，ングを行うには，リスト1 のように記述します．同様

grep などで採用されているIEEE 1003.2（POSIX.2）にereg系関数を利用する場合はリスト2 のように記述準拠のライブラリ関数を使用したereg 系関数，マルします．

チバイト文字による文字クラスなどをサポートした

preg 系・ereg 系関数ともに第1 引数の文字列で正mb̲ereg 系関数といった実装の異なる3 種類のインタ規表現を指定する点は同じですが，preg 系関数ではフェースが利用できます．
PHPらしくそれぞれ別々の関数を利用することにな正規表現を/などのデリミタでくくった文字列で指定する必要があります．

りますが，正規表現を利用したマッチング・置換・メタ文字による文字の指定・オプションによる動作の設▼リスト 1 preg 系関数でのマッチング▼リスト 2 ereg 系関数でのマッチング<?php$text = 'Hello World!';
if (preg_match("/Hello/", $text)) {// $textはHelloを含んでいる} else {// $textはHelloを含んでいない}
?>

<?php
$text = 'Hello World!';
if (ereg("Hello", $text)) {// $textはHelloを含んでいる} else {// $textはHelloを含んでいない}?>

160● WEB +DB PRESS Vol.19

PHP 編
preg 系関数と ereg 系関数を学ぶ▼リスト 3 preg̲replace 関数での置換▼リスト 4 ereg̲replace 関数での置換<?php$text = 'www.example.com';$replaced = preg_replace('/\.com/', ".jp", $text);<?php$text = 'www.example.com';$replaced = ereg_replace('\.com', ".jp", $text);

echo $replaced, "\n";?>echo $replaced, "\n";?>// "www.example.jp"// "www.example.jp"▼表 1 preg 系関数（PCRE ベース）
戻り値関数

int

preg_match(string regex, strig src [, array matches [, int flags]])

int

preg_match_all(string regex, string src, array matches [, int flags])

array

preg_grep(string regex, array src)

mixed

preg_replace(mixed regex, mixed replacement, mixed src [, int limit])

mixed

preg_replace_callback(mixed regex, mixed callback, mixed src [, int limit])array

preg_split(string regex, string src [, int limit [, int flags]])

array

preg_grep(string regex, array inputs)

stringpreg_quote(string src [, string delimiter])▼表 2 ereg 系関数（IEEE 1003.2 ベース）
戻り値関数

bool

ereg(string regex, string src [, array capture])

stringereg_replace(string regex, string replacement, string src)

arraysplit(string regex, string src [, int limit])

booleregi(string regex, string src [, array capture])

stringeregi_replace(string regex, string replacement, string src)

arrayspliti(string regex, string src [, int limit])

置換

callback 関数が存在したり，ereg 系関数では正規表
現のオプションの指定はなく別々の関数で動作を切り

preg 系関数で置換を行う場合はリスト3 のように替えるといった差異があることがわかります．各関数

preg̲replace 関数を使用します．preg̲replace 関数の詳細についてはPHP マニュアルの「Perl 互換の正の第1 引数に正規表現，第2 引数に置換後の文字列，規表現関数」および「正規表現（regex）関数（POSIX

第 3 引数に対象の文字列を指定します．置換結果は拡張サポート）」を参照してください注1．

preg̲replace関数の戻り値で取得できます．

メタ文字同様にereg系関数で置換を行う場合はリスト4のよ
うにereg̲replace関数を使用します．

preg系関数は第1章 表1に挙げた一般的な正規表現

リスト1 とリスト2，リスト3 とリスト4 をご覧いたのほとんどのに加え，Perl バージョン5.005 で実装さだくとpreg 系関数とereg 系関数の利用方法は，正規れていた正規表現の多くを利用できます．ただし表現の指定方法以外は同じに見えます．しかし表1 と\p{Prop}といったUnicode プロパティ・スクリプ

表2 で示すとおりpreg系にはマッチした文字列を引数

ト・ブロックなどのUnicode関連の機能は利用できまに，任意の関数を実行して置換できるpreg̲replace̲せん．

注 1）http://www.php.net/manual/ja/ref.pcre.php， http://www.php.net/manual/ja/ref.regex.php

WEB +DB PRESS Vol.19 ●161

特別企画

1

文字列処理はこれでバッチリ！
もちろん現在の正規表現で一般的になりつつあるグ

またpreg 系関数のメタ文字 \sは，\t，\n，\v，ループ化のみの括弧(?:...)や，(?=...)や(?!

\f，\r，\x20（スペース）にマッチしますが，\v...)といった「先読み」や，(?<=...)と(?>=...)

をサポートしないPerl とは\sがマッチする対象が異の「後読み」はサポートしています．リスト5 は否定なる場合があります．他にも細かな部分で差異や制限の先読みを利用して，「Message: 」に続く文字列がまたは拡張されている箇所もあるので，詳しくはPHP

hoge以外の英数字の場合にマッチします ．

マニュアルの「Perl互換の正規表現関数」の「パター

注2ン構文」を参照してください注3．
ereg系関数はさらに事情が異なり第1章 表1で示し▼表 3 ereg 系関数のメタ文字

た \d や \s といったメタ文 字 は利 用 できず，メタ文字役割^

文字列の先頭を示す$

文字列の末尾を示す.

任意の1文字にマッチ利用できます注4（表3）．またグループ化のみの括弧や[...]...のいずれかの文字にマッチ

前後読みなどのモダンな機能は利用できません．

[^...]...以外の文字にマッチ[[:alpha:]]a〜z，A〜Zのアルファベットにマッチ[[:digit:]]

0〜9の数字にマッチ[[:alnum:]]

0〜9，a〜z，A〜Zの英数字にマッチ中で/regex/iや/regex/xのように指定します．利[[:xdigit:]]

0〜9，a〜z，A〜Fの16進数で使用する英数字にマッチ用できるオプションと役割は表4 のとおりです．また[[:lower:]]

小文字のアルファベット

preg̲match 関数・reg̲match̲all 関数・reg̲split 関[[:upper:]]

大文字のアルファベット

数では，第4 引数で固有のオプションを指定して戻り[[:punct:]]

記号にマッチ（!\"#$%&'()*+,-./:;<=>?@[\\]^_`

値の形式を変更することができます．この戻り値を変[[:digit:]]や[[:space:]]といったPOSIX 文字クラスと，[[:<:]]などの拡張文字クラスのみがオプション
preg 系関数でのオプションは，正規表現文字列の{|}˜）

更するオプションについてはPHP マニュアルの「Perl[[:graph:]]

英数字と記号にマッチ（alnumとpunct）[[:print:]]

表示可能な文字にマッチ（graphと\x20（スペース））[[:space:]]

空白文字にマッチ（\t\n\v\f\rと\x20（スペース））[[:blank:]]

スペースとタブ文字にマッチ[[:cntrl:]]

制御コードにマッチ

ereg やereg̲replace，大文字と小文字を無視する場[[:<:]]

ワード境界の先頭

合には関数eregi，eregi̲replace というふうに関数を[[:>:]]

ワード境界の末尾使い分けます．

(...)

グループ化とキャプチャ|or条件?

0もしくは1つ▼リスト 5 preg 系関数で否定の先読み*0以上の繰り返し+1以上の繰り返し<?php$text = 'Message: UREEEEE';{n}n個の繰り返し{n,}n個以上の繰り返し{n,m}n個以上m個以下の繰り返し互換の正規表現関数」を参照してください．
前記したとおりereg 系関数には正規表現のオプシ
ョンはなく，大文字と小文字を区別する場合には関数// hoge以外で始まる英数字の文字列にマッチ
if (preg_match('/^Message: (?!hoge)\w+$/', $text)) {echo 'OK', "\n";}?>

注 2）(?!(?:hoge){1,64}$)\w+$のようにして，hoge が 65 回以上繰り返される場合は「よほど hoge を入力したかった」と見てマッチさせることもできます．いや，ホントどうでもいいんですが．
注 3）http://www.php.net/manual/ja/pcre.pattern.syntax.php
注 4）第 1 章では説明していませんが，.NET を除く Perl，Java，PHP の preg 系関数はともに，POSIX 文字クラスと[[:^alpha:]]などの否定文字クラスが利用できます（ただし Java の場合は \p{Alpha}や \P{Alpha}と指定します）．

162● WEB +DB PRESS Vol.19

PHP 編
preg 系関数と ereg 系関数を学ぶ
キャプチャ$1と\1の両方の形式で指定できますが，ereg 系関数では\1の形式だけがサポートされています．またマッチした文字列の一部分を取り出したり置換結果

preg 系・ereg 系関数ともに置換文字列を"（ダブルに埋め込みたい場合はキャプチャを使用します．マックォート）で与える場合は，\\2 (url: \\1)のよ

チングの際のキャプチャ結果はリスト6 のようにpreg

うに \（バックスラッシュ）でエスケープする必要が系・ereg 系関数ともに，第3 引数で指定したarray 型あります．

変数にキャプチャ結果を格納し参照することができます．

▼表 4 preg 系関数のオプション

キャプチャ用括弧が複数ある場合は，括弧の出現順に応じて$array[1]，$array[2]，$array[3]

オプション

役割//i

大文字と小文字を無視する…と取得したいインデックス番号を与えて個別にアク//m

行単位でマッチングするセスできます．

//s.（ドット）を改行文字にもマッチさせる関数preg̲replace やereg̲replace での文字列置換//x

正規表現中の空白やコメントを許可するの際にキャプチャを利用する場合は，リスト7 のよう//e

括弧の出現順に応じて$1，$2，$3…もしくは\1，//A

preg 系関数では置換時のキャプチャ結果の参照に先頭行からのみにマッチするアンカーマッチモードにする（\Aでも可）\2，\3…の文字を指定します．リスト7 を実行すると図1 のように出力します．

preg_replace関数の置換結果をPHPの式として評価し，その結果で置換するにキャプチャ結果を埋め込みたい位置にキャプチャ用//D$（ダラー）を文字列の末尾のみにマッチさせる//S与えられた正規表現をより詳細に最適化します//U+や*などの欲張りな量指定子を，非欲張りな量指定子に変更する

図 1 ●リスト 7 の実行結果% php ex7.php
preg: Here... (url: http://www.example.jp/)
ereg: Here... (url: http://www.example.jp/)//X\（バックスラッシュ）と文字の組み合わせがメタ文字として存在しない場合エラーにする//u

UTF-8の文字列としてマッチする▼リスト 6 preg 系関数，ereg 系関数でのキャプチャ<?php$text = 'Name: Regular Expressions';
if (preg_match('/^(\w+):\s+(.+)$/', $text, $capture)) {$key= $capture[1];$value = $capture[2];echo "preg: key= $key\n";echo "preg: value = $value\n";}if (ereg('^([[:alnum:]_]+):[[:space:]]+(.+)$', $text, $capture)) {$key= $capture[1];$value = $capture[2];echo "ereg: key= $key\n";echo "ereg: value = $value\n";}?>▼リスト 7 preg 系関数，ereg 系関数でのキャプチャ結果による置換<?php$text = '<a href="http://www.example.jp/">Here...</a>';$replaced = preg_replace('|<a href="(.+?)">(.+?)</a>|', '$2 (url: $1)', $text);
echo "preg: $replaced\n";$replaced = ereg_replace('<a href="([^"]+)">([^<]+)</a>', '\2 (url: \1)', $text);
echo "ereg: $replaced\n";?>

WEB +DB PRESS Vol.19 ●163

特別企画

1

文字列処理はこれでバッチリ！
を用意します（リスト8）．

利用例

ereg系関数を使用する場合はそれではWebアプリケーションに応用できるような例題を使って，PHP の正規表現をより具体的に見て^[0-9]{3}(-?([0-9]{2}|[0-9]{4}))?$といった正規表現を使用することになります．

いきましょう．ここからはサポートしているメタ文字

ひらがな・カタカナ・漢字の差異や柔軟性からpcre 系関数のみを取り上げます．

preg 系とereg 系関数ではマルチバイト文字によるデータのチェック文字クラスの指定やUnicode プロパティなどがサポー

正規表現で郵便番号をチェックしてみましょう．現

トされていないため，文字境界や文字コードブロック

在利用されている郵便番号は数字7 桁で表され，3 桁を意識しながら正規表現を使用する必要があります．

目と4 桁目の間に -（ハイフン）を入れます．また，

リスト9 はEUC-JP の文字列から，英数字・ひらが平成10 年1 月以前の旧郵便番号には5 桁のものと3 桁な・カタカナ・漢字の各文字種ごとのブロックを抽出のものがあります．現在利用されている7 桁の郵便番するサンプルです．

号を入力させる場合が多いと思いますが，この例では

リスト9 を実行すると第2章と同様に% php ex9.php

¡154

漢字

¡350-11

カナ¡350-1106

混¡35011

じりの¡3501106

文章といったパターンにマッチする正規表現をinput/^\d{3}(?:-?(?:\d{2}|\d{4}))?$/

します▼リスト 8 preg 系関数での郵便番号のチェック<?php$text = $argv[1];if (preg_match('/^\d{3}(?:-?(?:\d{2}|\d{4}))?$/', $text)) {echo "$text is postal code\n";} else {echo "$text is not postal code\n";}?>▼リスト 9preg 系関数での文字種ごとの抽出<?php$text = '漢字カナ混じりの文章をinputします'; // EUC-JP$pattern = '/([\x21-\x7E]+| # Latin-1(?:\xA4[\xA1-\xF3])+| # Hiragana(?:\xA5[\xA1-\xF6])+| # Katakana(?:[\xB0-\xF4][\x00-\xFF])+# Kanji)/x';if (preg_match_all($pattern, $text, $capture)) {echo join("\n", $capture[1]), "\n";}?>164● WEB +DB PRESS Vol.19

PHP 編
preg 系関数と ereg 系関数を学ぶ▼リスト 10preg 系関数での URL のマークアップ<?php$text = 'PHPのマニュアルはhttp://www.php.net/manual/で参照できます．';$replaced = preg_replace('|(https?://[-_.!˜*\'()a-zA-Z0-9;/?:@&=+$,%#]+)|','<a href="$1">$1</a>', $text);
echo $replaced, "\n";?>

図 2 ●リスト 10 の実行結果% php ex10.php
PHPのマニュアルは<a href="http://www.php.net/manual/">http://www.php.net/manual/</a>で参照できます．と分割した文字列を出力します．

IEEE 1003.2 準拠の拡張正規表現ライブラリベースのテキスト中の URL をハイパーリンクに置換する

ereg系関数の2つのインタフェースの利用方法を見てWeblogやWebメールなどの実装では，入力された指したため，あえてマルチバイト対応のmb̲ereg系関文章に含まれるURLをマークアップする場合がよくあきました．また，今回は正規表現のさわりの解説を目数は取り扱いませんでした．

ります．この場合はpreg̲replace など正規表現の置

近日リリースされるPHPバージョン5.xではmb̲ereg

換を使えばシンプルな記述で処理できます．リスト10系関数の実装にOniguruma ライブラリが利用されるはURL を簡単に表現した正規表現を使って，キャプ予定です．現行のPHPバージョン4.3.xのmb̲ereg系

チャしたURL をaタグでマークアップします．

関数で利用されているmbregex ライブラリやpreg 系

リスト10 を実行すると変数 $text に含まれるURL

関数で利用されているPCRE ライブラリとの差異や，「http://www.php.net/manual/」をa タグでマークアその機能をまとめた「PHPにおける正規表現」のドキップして出力します （図2）．

ュメントがいつかリリースされることを密かに期待し

注5

XML 文書を解析するています注6．#

PHPにはGNOMEのlibxmlを使用してXMLのDOMにアクセスする関数や，expat ライブラリを使用してXML 文書を解析する関数がすでに用意されています
が，必要な情報を取り出すだけであれば正規表現だけでも簡単に記述できます．リスト11 はRSS ファイルを解析し，新しい上位5件のタイトルとURLをリストアップします．

まとめ
現在PHPで利用できる3種類の正規表現インタフェースのうちPCRE ライブラリベースのpreg 系関数と，▼リスト 11preg 系関数での RSS の解析<?php$rss = file_get_contents('php://stdin');$pattern = '!
(?:<item>|<item\s.+?>).*?<title>(.*?)</title>.*?<link>(.*?)</link>.*?</item>!sx';
if (preg_match_all($pattern, $rss, $capture)) {for ($1 = 0; $i < count($capture[0]) && $i < 5; $i++) {$title = $capture[1][$i];$link = $capture[2][$i];echo "$title ($link)\n";}}?>

注 5）ここで使用している URL の正規表現は簡易的なものです．より正確な URL の正規表現は大崎博基さんの Web サイトの「Perl メモ」→「http URL の正規表現」を参照してください．この他にも Shift̲JIS の文字列にマッチする正規表現など有用な例と考察がたくさん掲載されています．http://www.din.or.jp/˜ohzaki/
注 6）あと，似た機能を提供する API がたくさんある，という状況が整理されると個人的にうれしいのは秘密です．

WEB +DB PRESS Vol.19 ●165