PHP Note
category: PHP言語仕様 今日のエラーメッセージ 文字列操作

正規表現は怖くない

created: 2008-11-22 | modified: 2008-11-22

正規表現ってあの記号の羅列っぽいところが苦手なんです。あまり長~いものだと知恵熱が出ます。でも、何年も続けてる甲斐あってようやくわかるようになってきました。良く使う正規表現パターンは限られますし、正規表現を使ったプログラミングのパターンも見えてきましたのでここいらでまとめてみました。


正規表現ってな~に?

まず「郵便番号」を例にしてみます。では、郵便番号はどんな文字で構成されてるのか、後輩に教えるつもりで表現してみてください。私なら「"3桁の数字" に続けて "-(ハイフン)" があり、つづけて "4桁の数字" で構成されている文字列」と表現します。他にもあるでしょうが大抵の方にはこれでわかってもらえると思います。

この説明がまさに正規表現です。ひとことで言うなら「文字列の構成を表現したもの」といえます。文章だとコンピュータにわからないのでコンピュータにわかるよう表現したのが記号の羅列に見えるこれです。

// 郵便番号の説明
// "3桁の数字" に続けて "-(ハイフン)" があり、
// つづけて "4桁の数字" で構成されている文字列

// 郵便番号の正規表現
[0-9]{3}-[0-9]{4}

まず "[]"(ブラケット)や "{}"(大カッコ)などは、"メタ文字" と呼ばれる、正規表現で特殊な役割を持つ文字です。この他には "^ . \ + * ? ( ! < > = | : ) , $" があります。

では正規表現パターンを順に見ていきます。"[" と "]" で囲まれた部分は "文字の集合" です。この中の "0-9" は "0から9の文字" つまり "数字" を表しています。続く "{" と"}"(大カッコ)で囲まれた部分はその "出現数" です。"{3}" ならば "3回続く" ことを表します。この組み合わせ "[0-9]{3}" で "0から9の文字が3回続く" つまり "3桁の数字" を表しています。

続いて "-"(ハイフン)。"[" と "]" の中の "-" は、文字の範囲を表す "から" の役割を持ちますが、"[" と "]" の外は、普通に文字の "-"(ハイフン)のことです。

残る "[0-9]{4}" は出現数が "{4}" に変わっただけで初めと同じ。"0から9の文字が4回続く" つまり "4桁の数字" を表しています。

これで郵便番号がどんな文字なのかコンピュータに伝えることが出来ます。正規表現に用いるメタ文字やその意味は、いつもこちらのサイトを参考にさせてもらっています。正規表現は習うより慣れろです。どうぞたくさん書いてたくさん試してください。

PHPで使える正規表現には PCRE(preg_**関数) と POSIX(ereg_**関数) の2種類があります。微妙に違いますが、参考になる資料が豊富なので PCRE の方が勉強しやすくおすすめです。この後も PCRE で進めています。

Perl や JavaScript は言語そのものに正規表現が備わっていて直接プログラムに正規表現を書きますが、PHPでは正規表現パターンの文字列を関数に渡して処理します。文字列なので、パターンの中にPHPで特殊な意味を持つ文字(\${}など)を含む場合は、正規表現として評価される前に文字列として評価されるので注意が必要です。
例えば正規表現パターンの中で "\" を単なる文字として評価させる場合、エスケープの役割をもつ "\" は2つ重ねて "\\" と書きます。PHPでも "\" はエスケープの役割を持ってるのでさらに "\" を重ねる必要があり、"\" 一文字を正規表現パターンで表そうとすると "\\\\" となります。
もうひとつ、PHPで "$" は変数の接頭文字の役割を持っています。例えば正規表現パターンの中の "$abc" は、"$abc" というパターンである以前に、変数 $abc の値と評価されます。"$" を文字として評価させたいなら、正規表現パターンを "'"(シングルクォーテーション) で囲みましょう。もちろん変数として評価させたい場合はこの限りではありません。


以降は、正規表現を用いたPHPプログラミングの事例を挙げてみました。

パターンが含まれているか?

もっとも簡単なところで、文字列の中に正規表現パターンに一致する文字が含まれているかどうかを判定する場合です。これには preg_match関数を使います。第一引数が正規表現パターン、第二引数が調査対象の文字列です。preg_match はパターンにマッチすれば 1 を、マッチしなければ 0 を返します。

// 調査対象の文字列
$str = '〒150-0021 東京都渋谷区';

// 郵便番号の正規表現
$pattern = '/[0-9]{3}-[0-9]{4}/';

if (preg_match($pattern, $str)) {
    echo '含まれる';
} else {
    echo '含まれない';
}

正規表現パターンが "/" で挟まれてますが、PCRE系正規表現のお約束です。


パターンに一致するか?

上記に似てますがこちらは文字列の始めから終わりまで完全に一致するか判定する場合です。フォームに入力された値の検証(バリデーション)に良く使います。

まず文章にしてみると「"3桁の数字" で "始まり"、次に "-" が続いて、次に "4桁の数字" で "終わる" 文字列」って具合になります。この正規表現はこうです。「含まれるの正規表現」と比べてみてください。

// 完全一致の正規表現
^[0-9]{3}-[0-9]{4}$

// 含まれるの正規表現
[0-9]{3}-[0-9]{4}

パターン始めの "^" は行頭に一致します。"^" の直後は "[0-9]"、それが "{3}" 続くので、"行頭が3桁の数字" つまり "3桁の数字で始まる" を表すことが出来ます。

パターン最後の "$" は行末に一致します。"$" の直前は "{4}" ですが何が4回続くかというと "[0-9]" なので、"行末が4桁の数字" つまり "4桁の数字で終わる" を表すことが出来ます。

"^" や "$" が無い場合、前後の文字や有無は問わないのに対し、始まりと終わりを明示することによって"完全に一致する"ことを確かめられます。完全一致のPHPのコードは、正規表現パターンが違うだけで先ほどとかわりありません。

// 調査対象の文字列
$str1 = '150-0085';
$str2 = '〒150-0021 東京都渋谷区';

// 郵便番号に完全一致の正規表現
$pattern = '/^[0-9]{3}-[0-9]{4}$/';

// $str1 は完全に一致するけど
if (preg_match($pattern, $str1)) {
    echo '$str1:完全に一致';
} else {
    echo '$str1:一致してない';
}

// $str2 は一致しない
if (preg_match($pattern, $str2)) {
    echo '$str2:完全に一致';
} else {
    echo '$str2:一致してない';
}

パターン始め以外の "^" やパターン最後以外の "$" には、行頭一致や行末一致の役割はありません。単に文字として評価されます。


パターンに一致する文字を獲得する

次は、正規表現パターンに一致した文字を獲得しプログラムの中で再利用する場合です。preg_matchの第三引数を使います。正規表現パターンにマッチする文字が第三引数で指定する変数に配列で格納されます。

// 調査対象の文字列
$str = '〒150-0021 東京都渋谷区';

// 郵便番号の正規表現
$pattern = '/[0-9]{3}-[0-9]{4}/';

// マッチした文字の格納場所
$match = array();

// パターンにマッチした文字が $match に格納される
preg_match($pattern, $str, $match);
var_export($match);

この出力はこうです。パターン全体にマッチする文字が インデックス:0 に格納されます。

array (
  0 => '150-0021',
)

パターンの一部分を取り出したいことがよくあります。郵便番号なら始めの3桁と終わりの4桁を分けて取り出したい場合など、抽出したいパターンをカッコで囲みます。

// 調査対象の文字列
$str = '〒150-0021 東京都渋谷区';

// 郵便番号の正規表現
$pattern = '/([0-9]{3})-([0-9]{4})/';

// マッチした文字の格納場所
$match = array();

// パターンにマッチした文字が $match に格納される
preg_match($pattern, $str, $match);
var_export($match);

この出力はこうなります。パターン全体にマッチする文字が インデックス:0 に、始めのカッコ内のパターンにマッチする文字がインデックス:1 に、次のカッコ内のパターンにマッチする文字がインデックス:2 にといった具合に格納されます。カッコは左から順に評価され、入れ子になっていても必ず左側のカッコから順にインデックスの数値がふられます。

array (
  0 => '150-0021',
  1 => '150',
  2 => '0021',
)

"("、")" (カッコ)は、抽出対象を明示する他に、正規表現パターンをグループ化する役割も兼ねています。単純にグループ化だけを行いたい場合は、始まりのカッコ直後に "?:" をつけて、"(?:パターン)" のようにすると抽出対象にはなりません。混乱しないよう抽出対象とグループ化のカッコは使い分けた方が良いと思います。またカッコを文字として使いたい場合は "\("、"\)" のように直前に "\" をつけてエスケープします。


パターンに一致する文字を別の文字に置き換える

次は、正規表現パターンに一致した文字を別の文字列に置き換える場合です。正規表現による文字列置換には、preg_replace関数を用います。郵便番号だと置き換える用途が浮かばないので、文中のURLをアンカータグ付きのURLに置き換えてみます。

// 対象の文字列
$str = 'URL: http://www.sound-uz.jp/php/ PHPの基礎体力';

// URLの正規表現パターン
$pattern = '/http:\/\/[0-9a-z_,.:;&=+*%$#!?@()~\'\/-]+/i';

// 置き換え文字列
$replace = '<a href="$0">$0</a>';

// preg_replaceの戻り値は置き換えられた文字
$replaced = preg_replace($pattern, $replace, $str);
var_export($replaced);

この出力はこうなります。

'URL: <a href="http://www.sound-uz.jp/php/">http://www.sound-uz.jp/php/</a> PHPの基礎体力'

まず置き換え文字列($replace)をみてください。この中の $0 がパターンにマッチした文字(URL)に置き換わってるのがわかると思います。これは「後方参照」といって、マッチした文字を再び処理の中で用いるための仕組みです。$ と 数字の組み合わせにより、パターンにマッチした文字を参照できます。

$0 は正規表現パターン全体にマッチする文字を表しています。もちろん $1$2$3 ... というのもあるわけですが、これはパターンの中で、カッコで囲まれたパターンの文字を表します。始めのカッコのパターンにマッチする文字が $1、2番目のカッコのパターンにマッチする文字が $2、といった順番に対応しており、パターンの一部を参照することが出来ます。
パターンの中で () で囲まれたパターンのグループを "サブパターン" と呼びます。

尚、$ を使った後方参照はPHP固有の表現です。他の言語の正規表現で後方参照は \0 を用いています。PHPでも \0 という表記を用いることが出来ますので、上の例を $replace = '<a href="\0">\0</a>'; としても同じ結果が得られます。
だだし \0 で困るのが後方参照のすぐ後に数字が続く場合です。1番目のカッコのパターンに '0' を付け加えた文字のつもりで '\10' と書いてしまうと10番目のカッコのパターンと判断されます。こんな場合は $ と大カッコを使って、'${1}0' と書いて、1番目のカッコのパターンに '0' を付け加えた文字を表します。

前後しましたが、URLの正規表現パターンを補足しておきます。http: はおなじみURLのプロトコル部分です。この後は // ですが正規表現パターン終わりの / と判断されないよう \ でエスケープして \/\/ となります。

続く[] 内はURLに用いることが出来る文字の集合です。[0-9] と同様 [a-z] は小文字の 'a' から 'z' までのこと。さらに記号を加えてURLで使える文字の集合としています。最後の \'\/- は注意が必要です。' は文字列終わりの ' と判断されないようエスケープし \' とします。/ はパターン終わりの / と判断されないようエスケープし \/ とします。- は集合の中では文字の範囲を示す役割を持っているので、単に文字として扱うには \- とするか、集合の一番最初又は一番最後に加えます。

+ は「1回以上連続する」ことを表します。+ の直前はURLに使える文字の集合なので、"URLに使えるいずれかの文字が1回以上連続する" ことを表しています。

正規表現パターン終わりの / の後の i は、"大文字小文字を区別しない" ことを指定する"パターン修飾子"です。iが無い時、大文字と小文字は別の文字として評価されます。/[a-z]+/i/[a-zA-Z]+/ はどちらも、大文字小文字の区別せず、1文字以上連続する英字にマッチします。


正規表現パターン例

最後に、良く使ってる正規表現パターンを挙げてみました。ひとつの用途に対していくつものパターンが存在します。また長いパターンもよく見れば小さなパターンの組み合わせです。要は郵便番号の説明のように「対象をどう的確に表現するか」が肝ですので、そうした考え方のヒントになればと思います。

メールアドレス
/^[-._a-zA-Z0-9\/]+@[-._a-z0-9]+\.[a-z]{2,4}$/
フォームに入力されたメルアドのバリデーション用パターンです。RFCで定義されてるメールアドレスの正規表現はとても長いですが、実用的な範囲はこれで足りるかと思います。
@ より後ろがドメインを表してますがココで新しいものが2つ出てきました。まず \.は、.(ドット)を表しています。[](ブラケット)の外で . は "任意の1文字" を表すので、エスケープしないとドットとして評価されません。
{2,4} は、先に出てきた {3} の仲間です。{3} は "3回連続する" でしたが、カンマに続けてもうひとつ数字を指定することで "連続回数の範囲" を指定できます。{2,4} は "2~4回連続する" ことを表します。この直前が [a-z] なので、"小文字の英字が2~4回連続する" を表しています。これは .jp.com.net.info などドメインの終端にマッチすることを期待しています。
蛇足ですが、@ の前の A-Z は @nifty会員さんのメルアド用です。メールは小文字でも届くようですが、バリデーションの際ハネてしまわないよう加えてます。\/ は 携帯電話のメルアド用です。
/^[-.\w\/]+@[-._[:lower:]\d]+\.[[:lower:]]{2,4}$/
表記方法が異なるだけで実は上と全く同じです。\w は単語(Word)を構成する _0-9a-zA-Z と同じで、\d は数字(Digit)を構成する 0-9 と同じです。
[:lower:] は文字の集合を名前であらわす「キャラクタクラス」のひとつで、"アルファベットの小文字"(a-z)を表します。a-z と書く方が短いけど。

URL
/^(?:http|https):\/\/[\w,.:;&=+*%$#!?@()~\'\/-]+$/
URLのバリデーション用パターンです。プロトコル部分を先ほどと変えてるのは HTTPS もマッチさせたいからです。(?:) でグループ化された中に、httphttps| で区切られています。|OR の役割をしていて、"http または https にマッチする" ことを表しています。もし ftp もマッチさせたければ、(?:http|https|ftp) と加えれば良いだけです。
/(http|https):\/\/([-._a-z\d]+\.[a-z]{2,4})([\w,.:;&=+*%$#!@()~\'\/-]*)\??([\w,.:;&=+*%$#!?@()~\'\/-]*)/
URLからプロトコル・ドメイン・リクエスト・クエリーを分けて抽出するためのパターンです。後方参照のためグループ化のカッコには ?: をつけていません。
始めのサブパターンはプロトコルです。:// は抽出する必要が無いのでカッコから外してます。続くサブパターンはドメインです。メルアドの @ 以降と同じパターンを使っています。
次のサブパターンですが、この中の文字の集合はURLで使える文字から ? を除いた物です。? 以降がクエリストリングスなのでそれ以外の文字が続く間はリクエストだと判断できます。直後の * は "0回以上連続する" ことを表します。+ は "1回以上連続" だったので少なくとも1回は出現しないとマッチしませんが、* はまったく出現しなくてもマッチします。ドメインよりも後は無い場合もあるので + ではなく * が適切です。
クエリーストリングスがあれば ? と続くわけですが、\? としてるのは、? が文字集合の外では他の正規表現を修飾して "最も短いパターン" にマッチする役割を持つからです。この直後の ? がまさしくそれで、\?? で "?が1回出現するかまったく出現しない" ことにマッチさせています。
そして ? の後に続く、URLで使える文字の集合をクエリーストリングスとしています。無い場合もありえるのでココも * で "0回以上の連続" にマッチさせています。
// このパターンを使うとこんな具合になります。
$pattern = '/(http|https):\/\/([-._a-z\d]+\.[a-z]{2,4})([\w,.:;&=+*%$#!@()~\'\/-]*)\??([\w,.:;&=+*%$#!?@()~\'\/-]*)/';

$str = 'http://www.sound-uz.jp/php/index.php?abc=123&def=456';

$match = array();

preg_match($pattern, $str, $match);
var_export($match);

/* 出力
array (
  0 => 'http://www.sound-uz.jp/php/index.php?aaa=123&def=456',
  1 => 'http',
  2 => 'www.sound-uz.jp',
  3 => '/php/index.php',
  4 => 'abc=123&def=456',
)
*/

URLを要素に分解する parse_url があるので、バリデーション用パターンで全体を取り出してからこの関数を用いることも検討してみましょう。


電話番号
/^0\d{1,4}[-(]?\d{1,4}[-)]?\d{3,4}$/
電話番号のバリデーション用パターンです。数字とハイフンくらいと思ってなめてかかるとハマります。正規表現よりも電話番号をどう定義するかがとても重要となります。
電話をかける際は数字だけでいいので最もコンパクトに表現すると数字だけが10桁または11桁連続するパターンがあります。市外局番は必ずゼロで始まるので 0\d{9,10} となります。
最も長い場合は市外局番・市内局番・お客様番号の間をハイフンで区切るパターンがあります。市内局番をカッコで囲むパターンもよく見かけます。ただ固定電話・携帯・IP電話・フリーダイヤルどれも区切りが入る位置がまちまちなので、それぞれの桁数を考えながら区切り位置を決めます。
市外局番は最小2桁・最大4桁、市内局番も最小2桁・最大4桁、残りはフリーダイヤルもあるので最小3桁・最大4桁となるでしょう。(局番区切りの間違いを訂正。たけるさんありがとうございました。[4256] 正規表現のページ
市外局番は最小2桁・最大5桁、市内局番は最小1桁・最大4桁、残りはフリーダイヤルもあるので最小3桁・最大4桁となるでしょう。
これに区切りの文字を加えて上の完成形となりますが、これだと 012345 にも 0123456789012 にもマッチしてしまいます。いくら電話番号っぽい正規表現になっていてもこれではチェックの役目を果たしません。そこで根本から考え直してみました。
preg_replace('/[^\d]+/', '', $phone)
チェックする値はユーザがフォームに入力した電話番号。区切りが何であろうとゼロで始まる10桁または11桁の数字させあれば電話をかけられます。つまり電話番号っぽくなくても数字が10個または11個含まれていることが最重要なわけです。そこで正規表現だけで確認するのは止めて、数字以外の文字を除き残った文字を数えることにしました。
上の preg_replace$phone に含まれる数字以外の文字を取り除きます。あとは文字数と始めのゼロを確かめます。電話番号っぽい正規表現よりもこのほうがシンプルでずっと実用的です。
[^\d] は初めて出てきたので補足します。[ のすぐ後に ^ があると "集合以外の文字" にマッチします。ここでは \d0-9)以外の文字を '' に置き換えることで取り除いています。
// 電話番号のバリデーション

$phone = '01-2345-6789';

$number = preg_replace('/[^\d]+/', '', $phone);

if ('0' === $number{0} && (10 == strlen($number) || 11 == strlen($number))) {
	echo 'OK: ',$number;
} else {
	echo 'BAT!: ',$number;
}

タグ
/<a href="([^"]+)">(.+?)<\/a>/is
HTMLドキュメントからアンカータグのURLを抽出するパターンです。「" で囲まれた部分」は、「" の後に続く、" 以外の文字」と考えることが出来ます。href=" に続く [^"]+ がURLなので、href="([^"]+)" というパターンになります。特定の文字で囲まれた文字列にマッチさせたい時は、「囲み文字以外の文字が続く」と考えると使いまわしがききます。
あとこれ (.+?) は、タグに囲まれた文字も一緒に抽出する為のパターンです。.(ドット)は "任意の一文字" にマッチします。.+ はその連続となりますが、どんな文字にもマッチするので </a> にもマッチしてしまいます。これではHTMLドキュメントにいくつもアンカータグが含まれていた場合、パターン <\/a> は最後に現れた </a> にマッチしてしまいます。これは出来る限り多くマッチしようとする正規表現の性質によるものです。
開始タグ以降、始めに登場する </a> にマッチさせるため、.+? というパターンになります。+*? 付加すると、"任意の連続する文字"が"最短"でマッチするようになります。
パターン修飾子 "s" は "." を改行コードにもマッチさせるためのものです。

アンカータグを全部抽出するため preg_match_all を使います。抽出した値をどう処理するかによって第4引数のフラグを指定します。

$html = '
<ul>
    <li><a href="http://www.yahoo.co.jp/">Yahoo!JAPAN</a></li>
    <li><a href="http://www.sound-uz.jp/php/">PHPの基礎体力</a></li>
    <li><a href="http://beatnik.jp/blog/">PHPで翻訳三昧</a></li>
</ul>
';

$pattern = '/<a href="([^"]+)">(.+?)<\/a>/is';

$match = array();

preg_match_all($pattern, $html, $match, PREG_SET_ORDER);
var_export($match);

PREG_SET_ORDER だと出力はこうなります。

array (
  0 => 
  array (
    0 => '<a href="http://www.yahoo.co.jp/">Yahoo!JAPAN</a>',
    1 => 'http://www.yahoo.co.jp/',
    2 => 'Yahoo!JAPAN',
  ),
  1 => 
  array (
    0 => '<a href="http://www.sound-uz.jp/php/">PHPの基礎体力</a>',
    1 => 'http://www.sound-uz.jp/php/',
    2 => 'PHPの基礎体力',
  ),
  2 => 
  array (
    0 => '<a href="http://beatnik.jp/blog/">PHPで翻訳三昧</a>',
    1 => 'http://beatnik.jp/blog/',
    2 => 'PHPで翻訳三昧',
  ),
)

マルチバイト文字の正規表現パターン

正規表現パターンにマルチバイト文字を扱う際は mb_ereg_**関数を使うことになります。ここまでPCRE系を使ってきておいて申し訳ありませんが、こちらは POSIX系の正規表現です。PCRE系とあきらかに違うところは、パターンを囲む "/" が無いところと、パターン修飾子を引数で指定するところです。その他は処理系ごとにも違うのでその都度確かめてください。

マルチバイト文字を扱う際忘れてならないのが文字エンコーディングです。あらかじめ正規表現で扱う文字エンコーディングを mb_regex_encoding関数で指定します。

フリガナ

マルチバイト文字に正規表現が必要なシーンは限られてると感じます。私が最も使ってるのは、フォームに入力された「フリガナ」が「カタカナ」か確かめる時です。

^[ ァ-ヶー]+$
文字列が "全角カタカナ" + "スペース" だけで構成されてるかをチェックするパターンです。
^[ ぁ-んー]+$
文字列が "全角ひらがな" + "スペース" だけで構成されてるかをチェックするパターンです。ひらがなよりもカタカナの方が文字が多いので、フリガナにはカタカナを用いるべきです。"ヴ"や"ヶ" は "ひらがな" にはありません。
[。-゚]
半角カナが含まれているかチェックするパターン。半角カナは濁点や半濁点も1文字なので、検索や並べ替え・文字列を部分的に取り出す場合など、プログラムを複雑にする要因となります。可能ならすべて"全角カナ"に統一を図る方が吉です。

これらを踏まえ、ユーザが入力した「フリガナ」をチェックする際は、あらかじめ「半角カナ⇒全角カナ」と「ひらがな⇒カタカナ」変換をかけたあとに "全角カナ" かチェックしています。ユーザからの入力は変換しないのが基本ですが、このような場合は変換にひと手間かけたほうがユーザに親切ですし、入力可能な文字の制限を面倒に感じてフォームから離脱するユーザを減らせます。

mb_internal_encoding('EUC-JP');
mb_regex_encoding('EUC-JP');

$kana = $_POST['kana'];
$kana = mb_convert_kana($kana, 'KVCs');

if (mb_ereg('^[ ァ-ヶー]+$', $kana)) {
    echo 'OK';
} else {
    echo 'BAT!';
}

// 逆パターンでチェックする方法もあります。
// " ァ-ヶー" 以外の文字が含まれていれば、BAT!。
// 但し、$kana = '' でも OKとなります。
if (mb_ereg('[^ ァ-ヶー]', $kana)) {
    echo 'BAT!';
} else {
    echo 'OK';
}

日付
(明治|大正|昭和|平成|\'|[MTSHmtsh]\.?)?([\d0-9]{2,4})[年/.-]([\d0-9]{1,2})[月/.-]([\d0-9]{1,2})日?
文章から日付を抽出するパターンです。年号のところは西暦の場合無いので "?" をつけています。あとはマルチバイト文字を混ぜて数字と区切りの文字という構成です。
mb_internal_encoding('EUC-JP');
mb_regex_encoding('EUC-JP');

$text = 'ジョージ・リンチさんの生年月日は1954年9月18日です。';

$pattern = '(明治|大正|昭和|平成|[MTSHmtsh]\.?)?([\d0-9]{1,4})[年/.-]([\d0-9]{1,2})[月/.-]([\d0-9]{1,2})日?';

$match = array();

mb_ereg($pattern, $text, $match);
var_export($match);


/* 出力
array (
  0 => '1954年9月18日',
  1 => false,
  2 => '1954',
  3 => '9',
  4 => '18',
)
*/

正規表現に、これで完璧という解答はありません。日付でも、西暦は、はじめの2桁を省略して 「'98」 のように表現することがあります。日付の区切り文字の「/.-」が全角の場合もあるでしょう。これらを全て正規表現で補うのか、あらかじめ書式を統一しておくのか、といったロジックをつめていく過程も正規表現をつかったプログラミングの醍醐味でもあります。

正規表現は結構負荷の高い処理です。乱発はパフォーマンス低下につながります。特定の文字が含まれるか判定するだけなら "strpos" や "strstr" で出来ますし、単純な文字列の置き換えなら "str_replace" や "strtr" で十分です。正規表現が本当に必要か、別の方法はないかよく検討してみましょう。


category: PHP言語仕様 今日のエラーメッセージ 文字列操作
PHP Note
ページの一番上へ
Googleグックマークに登録 Yahooグックマークに登録 livedoorクリップに登録 @niftyクリップに登録 はてなブックマークに登録 deliciousに登録 Buzzurlに登録 FC2ブックマークに登録
最近更新したNote
よく読まれている記事
Yahoo Search

最近更新された掲示板トピックス
PHPマニュアル
今日のブックマーク
PHPマニュアル関数検索
関数名を入力し検索ボタンをクリック↑