正規表現は、テキストを調べて変更するための強力なツールです。正規表現自体は、ミニ プログラミング言語のような一般的なパターン表記を使用して、テキストを記述および解析できます。これらを使用すると、文字列内のパターンを検索して、一致を柔軟かつ正確に抽出できます。ただし、正規表現はより強力なため、より基本的な文字列関数よりも遅くなることに注意してください。特定の必要がある場合にのみ、正規表現を使用してください。
このチュートリアルでは、基本的な正規表現構文の概要を簡単に説明し、正規表現を操作するために PHP が提供する関数について検討します。
The Basics
Matching Patterns
Replacing Patterns
Array Processing
PHP は、POSIX 拡張正規表現と Perl 互換正規表現 (PCRE) の 2 種類の正規表現をサポートしています。PCRE 関数は POSIX 関数よりも強力で高速であるため、それらに集中します。
基礎
正規表現では、ほとんどの文字は自分自身のみに一致します。たとえば、文字列「ジョン プレーズ フットボール」で正規表現「foo」を検索すると、その文字列に「foo」が含まれているため、一致が得られます。一部の文字は、正規表現で特別な意味を持ちます。たとえば、ドル記号 ($) は、特定のパターンで終わる文字列と一致させるために使用されます。同様に、正規表現の先頭にあるキャレット (^) 文字は、文字列の先頭と一致する必要があることを示します。自分自身に一致する文字はリテラルと呼ばれます。特別な意味を持つ文字はメタ文字と呼ばれます。
ドット (.) メタ文字は、改行 () を除く任意の 1 文字と一致します。したがって、パターン ht は、hat、hothit、hut、h7t などに一致します。垂直パイプ (|) メタ文字は、正規表現の代替として使用されます。これは論理 OR 演算子のように動作するため、複数の文字セットに一致するパターンを作成する場合に使用する必要があります。たとえば、パターン Utah|Idaho|Nevada は、"Utah"、"Idaho"、または "Nevada" を含む文字列に一致します。括弧は、シーケンスをグループ化する方法を提供します。たとえば、(Nant|b)ucket は "Nantucket" または "bucket" に一致します。括弧を使用して文字をグループ化して代替することをグループ化と呼びます。
パターン内のリテラル メタ文字に一致させたい場合は、バックスラッシュでエスケープする必要があります。
パターンで許容される文字のセットを指定するには、自分で文字クラスを作成するか、定義済みの文字クラスを使用できます。文字クラスを使用すると、一連の文字を正規表現の 1 つのアイテムとして表すことができます。使用可能な文字を角括弧で囲むことにより、独自の文字クラスを作成できます。文字クラスは、クラス内の任意の文字に一致します。たとえば、文字クラス [abc] は a、b、または c に一致します。文字の範囲を定義するには、最初と最後の文字をハイフンで区切って入力します。たとえば、すべての英数字に一致させる場合: [a-zA-Z0-9]。クラスにない任意の文字に一致する否定文字クラスを作成することもできます。否定文字クラスを作成するには、文字クラスを ^: [^0-9] で始めます。
メタ文字 +、*、?、および {} は、パターンが一致する回数に影響します。+ は「前の式の 1 つ以上に一致」を意味し、* は「前の式の 0 個以上に一致」を意味し、? は、「前の式の 0 個または 1 個に一致する」ことを意味します。中括弧 {} は別の方法で使用できます。単一の整数の場合、{n} は「前の式の正確に n 回の出現に一致する」ことを意味し、1 つの整数とカンマの場合、{n,} は「前の式の n 回以上の出現に一致する」ことを意味し、カンマで区切られた 2 つのintegers {n,m} は、「前の文字が n 回以上 m 回以下の場合に一致する」ことを意味します。
次に、例を見てみましょう。
Regular Expression Will match...
foo The string "foo"
^foo "foo" at the start of a string
foo$ "foo" at the end of a string
^foo$ "foo" when it is alone on a string
[abc] a, b, or c
[a-z] Any lowercase letter
[^A-Z] Any character that is not a uppercase letter
(gif|jpg) Matches either "gif" or "jpeg"
[a-z]+ One or more lowercase letters
[0-9\.\-] Аny number, dot, or minus sign
^[a-zA-Z0-9_]{1,}$ Any word of at least one letter, number or _
([wx])([yz]) wy, wz, xy, or xz
[^A-Za-z0-9] Any symbol (not a number or a letter)
([A-Z]{3}|[0-9]{4}) Matches three letters or four numbers
Perl 互換の正規表現は、パターンの Perl 構文をエミュレートします。つまり、各パターンを区切り記号のペアで囲む必要があります。通常、スラッシュ (/) 文字が使用されます。たとえば、/パターン/。
PCRE 機能は、マッチング、置換、分割、フィルタリングなど、いくつかのクラスに分けることができます。
マッチングパターン
preg_match() 関数は、文字列に対して Perl スタイルのパターン マッチングを実行します。preg_match() は、2 つの基本パラメーターと 3 つのオプション・パラメーターを取ります。これらのパラメータは、順番に、正規表現文字列、ソース文字列、一致を格納する配列変数、フラグ引数、および検索を開始する別の場所を指定するために使用できるオフセット パラメータです: preg_match ( pattern,件名 [, 一致する [, フラグ [, オフセット]]])
preg_match() 関数は、一致が見つかった場合は 1 を返し、それ以外の場合は 0 を返します。文字列「Hello World!」を検索してみましょう。文字「ll」の場合:
<?php
if (preg_match("/ell/", "Hello World!", $matches)) {
echo "Match was found <br />";
echo $matches[0];
}
?>
文字 "ll" は "Hello" に存在するため、preg_match() は 1 を返し、$matches 変数の最初の要素はパターンに一致した文字列で埋められます。次の例の正規表現は文字 "ell" を探していますが、次の文字でそれらを探しています:
<?php
if (preg_match("/ll.*/", "The History of Halloween", $matches)) {
echo "Match was found <br />";
echo $matches[0];
}
?>
次に、より複雑な例を考えてみましょう。正規表現の最も一般的な用途は検証です。以下の例では、パスワードが「強力」であるかどうかをチェックします。つまり、パスワードは 8 文字以上で、少なくとも 1 つの小文字、1 つの大文字、および 1 つの数字を含む必要があります。
<?php
$password = "Fyfjk34sdfjfsjq7";
if (preg_match("/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$/", $password)) {
echo "Your passwords is strong.";
} else {
echo "Your password is weak.";
}
?>
^ と $ は、文字列の最初と最後で何かを探しています。「.*」の組み合わせは、開始と終了の両方で使用されます。前述のように、.(ドット) メタ文字は任意の英数字を意味し、* メタ文字は「0 個以上」を意味します。間には括弧内のグループがあります。「?=」の組み合わせは、「次のテキストはこうでなければならない」という意味です。このコンストラクトはテキストをキャプチャしません。この例では、表示される順序を指定する代わりに、表示する必要があることを示していますが、順序は気にしません。
最初のグループ化は (?=. {8,}) です。これは、文字列に少なくとも 8 文字があるかどうかをチェックします。次のグループ化 (?=. [0-9]) は、「任意の英数字が 0 回以上発生し、次に任意の数字が発生する可能性がある」ことを意味します。したがって、これは文字列に少なくとも 1 つの数字があるかどうかをチェックします。ただし、文字列はキャプチャされないため、その 1 つの数字は文字列のどこにでも現れる可能性があります。次のグループ化 (?=. [az]) および (?=. [AZ]) は、文字列内の任意の場所に応じて小文字と大文字を探します。
最後に、電子メール アドレスを検証する正規表現を検討します。
<?php
$email = firstname.lastname@aaa.bbb.com;
$regexp = "/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/";
if (preg_match($regexp, $email)) {
echo "Email address is valid.";
} else {
echo "Email address is <u>not</u> valid.";
}
?>
この正規表現は、先頭の数字をチェックし、電子メール アドレスのユーザー名とドメイン名の複数のピリオドもチェックします。この正規表現を自分で調べてみましょう。
速度上の理由から、preg_match() 関数は、文字列内で最初に見つかったパターンのみに一致します。これは、文字列にパターンが存在するかどうかを非常に迅速に確認できることを意味します。別の関数である preg_match_all() は、パターンが許容する回数だけ文字列に対してパターンを照合し、一致した回数を返します。
パターンの置き換え
上記の例では、検索文字列はそのままにして、文字列内のパターンを検索しました。preg_replace() 関数は、パターンに一致する部分文字列を探し、それらを新しいテキストに置き換えます。preg_replace() は、3 つの基本パラメーターと追加の 1 つのパラメーターを取ります。これらのパラメータは、順番に、正規表現、見つかったパターンを置き換えるテキスト、変更する文字列、および置き換えられる一致の数を指定する最後のオプションの引数です。preg_replace(パターン、置換、件名[、制限])
この関数は、一致が見つかった場合は変更された文字列を返し、それ以外の場合は元の文字列の変更されていないコピーを返します。次の例では、コピーライト フレーズを検索し、年を現在の年に置き換えます。
<?php
echo preg_replace("/([Cc]opyright) 200(3|4|5|6)/", "$1 2007", "Copyright 2005");
?>
上記の例では、置換文字列で後方参照を使用しています。後方参照を使用すると、一致したパターンの一部を置換文字列で使用できます。この機能を使用するには、括弧を使用して、使用する可能性のある正規表現の要素をラップする必要があります。サブパターンに一致するテキストは、ドル記号 ($) とサブパターンの番号で参照できます。たとえば、サブパターンを使用している場合、$0 は一致全体に設定され、$1、$2 などは各サブパターンの個々の一致に設定されます。
次の例では、日付形式を「yyyy-mm-dd」から「mm/dd/yyy」に変更します。
<?php
echo preg_replace("/(\d+)-(\d+)-(\d+)/", "$2/$3/$1", "2007-01-25");
?>
また、サブジェクトとして文字列の配列を渡して、それらすべてを置換することもできます。preg_replace() への 1 回の呼び出しで、同じ文字列または文字列の配列に対して複数の置換を実行するには、パターンと置換の配列を渡す必要があります。例を見てください:
<?php
$search = array ( "/(\w{6}\s\(w{2})\s(\w+)/e",
"/(\d{4})-(\d{2})-(\d{2})\s(\d{2}:\d{2}:\d{2})/");
$replace = array ('"$1 ".strtoupper("$2")',
"$3/$2/$1 $4");
$string = "Posted by John | 2007-02-15 02:43:41";
echo preg_replace($search, $replace, $string);?>
上記の例では、別の興味深い機能を使用しています。PHP に対して、置換が行われた後に一致テキストを PHP コードとして実行するように指示できます。正規表現の末尾に「e」を追加したため、PHP は作成した置換を実行します。つまり、strtoupper(name) を受け取り、それを strtoupper() 関数の結果である NAME に置き換えます。
配列処理
PHP の preg_split() 関数を使用すると、文字通りの文字列よりも複雑なものに基づいて文字列を分割できます。文字列を固定式ではなく動的式で分割する必要がある場合、この関数が役に立ちます。基本的な考え方は preg_match_all() と同じですが、対象の文字列の一致した部分を返す代わりに、指定されたパターンに一致しなかった部分の配列を返します。次の例では、正規表現を使用して、任意の数のコンマまたはスペース文字で文字列を分割しています。
<?php
$keywords = preg_split("/[\s,]+/", "php, regular expressions");
print_r( $keywords );
?>
もう 1 つの便利な PHP 関数は preg_grep() 関数で、指定されたパターンに一致する配列の要素を返します。この関数は入力配列を走査し、指定されたパターンに対してすべての要素をテストします。一致が見つかった場合、一致する要素は、すべての一致を含む配列の一部として返されます。次の例では、配列と文字 AJ で始まるすべての名前を検索します。
<?php
$names = array('Andrew','John','Peter','Nastin','Bill');
$output = preg_grep('/^[a-m]/i', $names);
print_r( $output );
?>