PHP の正規表現関数
PHP には 2 セットの正規表現関数ライブラリがあります。 1 つのセットは PCRE (Perl 互換正規表現) ライブラリによって提供されます。 PCRE ライブラリは、Perl と同じ構文規則を使用し、接頭辞「preg_」が付いた名前の関数を使用して正規表現パターン マッチングを実装します。もう 1 つのセットは、POSIX (ポータブル オペレーティング システム インターフェイス) 拡張ライブラリによって提供されます。 POSIX 拡張正規表現は POSIX 1003.2 で定義されており、通常は「ereg_」接頭辞が付いた名前の関数を使用します。
2 つの関数ライブラリ セットの関数は似ていますが、実行効率が若干異なります。一般に、同じ機能を実現するには、PCRE ライブラリを使用した方が効率がわずかに優れています。その使用法については以下で詳しく説明します。
正規表現マッチング
1. preg_match()
関数プロトタイプ: int preg_match (string $pattern, string $content [, array $matches])
preg_match () 関数は、$content 文字列内で、$pattern で指定された正規表現に一致するコンテンツを検索します。 $matches が指定されている場合、一致する結果がそこに配置されます。 $matches[0] にはパターン全体に一致するテキストが含まれ、$matches[1] には括弧で囲まれたパターン要素の最初にキャプチャされた一致が含まれます。この関数は 1 つの一致のみを実行し、最終的に一致結果の数 0 または 1 を返します。リスト 6.1 は、preg_match() 関数のコード例を示しています。
コード 6.1 日付と時刻の一致
// 照合する必要がある文字列。 date 関数は現在時刻を返します
$content = "現在の日付と時刻は ".date("Y-m-d h:i a") です。"、私たちは一緒に PHP を学習しています。";
//時刻に合わせていつもの方法で
If (preg_match ("/d{4}-d{2}-d{2} d{2}:d{2} [ap]m/", $content, $m))
{
echo "一致する時間は次のとおりです:" .$m[0] "n";
}
//時間パターンが明らかなので単純に合わせる事も可能
If (preg_match ("/([d-]{10}) ([d:]{5} [ap]m)/", $content, $m))
{
echo "現在の日付は次のとおりです:" .$m[1] "n";
echo "現在時刻は次のとおりです:" .$m[2] "n";
}
?>
これは、単純な動的テキスト文字列マッチングの例です。現在のシステム時刻を「2006年8月17日13時25分」とすると、以下の内容が出力されます。
一致時刻は: 2006-08-17 01:25 pm
現在の日付: 2006-08-17
現在の時刻: 午後 1 時 25 分
2. ereg() と eregi()
ereg() は、POSIX 拡張ライブラリの正規表現マッチング関数です。 eregi() は、ereg() 関数の大文字と小文字を無視するバージョンです。どちらも preg_match と同様の関数を持っていますが、この関数は一致が成功したかどうかを示すブール値を返します。 POSIX 拡張ライブラリ関数の最初のパラメータは正規表現文字列を受け入れる、つまり区切り文字は必要ないことに注意してください。たとえば、リスト 6.2 はファイル名のセキュリティをチェックする方法です。
コード6.2 ファイル名のセキュリティチェック
$username = $_SERVER['REMOTE_USER'];
$filename = $_GET['file'];
//システムのセキュリティを確保するためにファイル名をフィルタリングします
If (!ereg('^[^./][^/]*$', $userfile))
{
die('これは違法なファイル名ではありません!');
}
//ユーザー名をフィルタリングします
If (!ereg('^[^./][^/]*$', $username))
{
die('これは無効なユーザー名ではありません');
}
//セキュリティフィルタリングを通じてファイルパスを照合します
$thefile = "/home/$username/$filename";
?>
通常、Perl 互換の正規表現マッチング関数 perg_match() を使用すると、ereg() または eregi() を使用するより高速になります。文字列に特定の部分文字列が含まれているかどうかだけを確認したい場合は、strstr() 関数または strpos() 関数を使用することをお勧めします。
3. preg_grep()
関数プロトタイプ: 配列 preg_grep (文字列 $pattern, 配列 $input)
preg_grep() 関数は、指定された $pattern パターンに一致する $input 配列内のセルを含む配列を返します。 Preg_grep() は、入力配列 $input 内の各要素の照合のみを実行します。リスト 6.3 は、preg_grep() 関数の使用法を簡単に説明する例を示しています。
コード 6.3 配列クエリのマッチング
$subjects = array(
「機械工学」「医学」
「社会科学」、「農業」、
「商学」「政治」
);
//匹配所有仅由有一个单词组成的科目名
$alonewords = preg_grep("/^[a-z]*$/i", $subjects);
?>
6.3.2 进行全局正则表达式匹配
1.preg_match_all()
与preg_match()函数类似。如果使用了第三个参数,将把所有可能的匹配结果放入。本函数返回整个模式匹配的次数(可能为0),如果出错返回False。下面是一个将文本中的URL链接地址转换为HTML代码的示例。代码6.4是preg_match_all()函数的使用范例。
代码6.4 将文本中的链接地址转成HTML
//功能:将文本中的链接地址转成HTML
//输入:字符串
//输出:字符串
function url2html($text)
{
//匹配一个URL,直到出现空白为止
preg_match_all("/http:\/\/?[^\s]+/i", $text, $links);
//设置页面显示URL地址的长度
$max_size = 40;
foreach($links[0] as $link_url)
{
//计算URL的长度。如果超过$max_size的设置,则缩短。
$len = strlen($link_url);
if($len > $max_size)
{
$link_text = substr($link_url, 0, $max_size)."...";
} else {
$link_text = $link_url;
}
//生成HTML文字
$text = str_replace($link_url,"$link_text",$text);
}
return $text;
}
//运行实例
$str = “这是一个包含多个URL链接地址的多行文字。欢迎访问http://www.taoboor.com”;
print url2html($str);
/*输出结果
这是一个包含多个URL链接地址的多行文字。欢迎访问
http://www.taoboor.com
*/
?>
2.多行匹配
仅仅使用POSIX下的正则表式函数,很难进行复杂的匹配操作。例如,对整个文件(尤其是多行文本)进行匹配查找。使用ereg()对此进行操作的一个方法是分行处理。代码6.5的示例演示了ereg()如何将INI文件的参数赋值到数组之中。
代码6.5 文件内容的多行匹配
$rows = file('php.ini'); //将php.ini文件读到数组中
//循环遍历
foreach($rows as $line)
{
If(trim($line))
{
//将匹配成功的参数写入数组中
if(eregi("^([a-z0-9_.]*) *=(.*)", $line, $matches))
{
$options[$matches[1]] = trim($matches[2]);
}
unset($matches);
}
}
//输出参数结果
print_r($options);
?>
提示
这里只是为了方便说明问题。解析一个*.ini文件,最佳方法是使用函数parse_ini_file()。该函数直接将*.ini文件解析到一个大数组中。
6.3.3 正则表达式的替换
1.ereg_replace()和eregi_replace()
函数原型:string ereg_replace (string $pattern, string $replacement, string $string)
string eregi_replace (string $pattern, string $replacement, string $string)
ereg_replace()在$string中搜索模式字符串$pattern,并将所匹配结果替换为$replacement。当$pattern中包含模式单元(或子模式)时,$replacement中形如“\1”或“$1”的位置将依次被这些子模式所匹配的内容替换。而“\0”或“$0”是指整个的匹配字符串的内容。需要注意的是,在双引号中反斜线作为转义符使用,所以必须使用“\\0”,“\\1”的形式。
eregi_replace()和ereg_replace()的功能一致,只是前者忽略大小写。代码6.6是本函数的应用实例,这段代码演示了如何对程序源代码做简单的清理工作。
代码6.6 源代码的清理
$lines = file('source.php'); //将文件读入数组中
for($i=0; $i<count($lines); $i++)
{
//将行末以“\\”或“#”开头的注释去掉
$lines[$i] = eregi_replace("(\/\/|#).*$", "", $lines[$i]);
//将行末的空白消除
$lines[$i] = eregi_replace("[ \n\r\t\v\f]*$", "\r\n", $lines[$i]);
}
//整理后输出到页面
echo htmlspecialchars(join("",$lines));
?>
2.preg_replace()
函数原型:mixed preg_replace (mixed $pattern, mixed $replacement, mixed $subject [, int $limit])
preg_replace较ereg_replace的功能更加强大。其前三个参数均可以使用数组;第四个参数$limit可以设置替换的次数,默认为全部替换。代码6.7是一个数组替换的应用实例。
代码6.7 数组替换
//字符串
$string = "Name: {Name}<br>\nEmail: {Email}
\nAddress: {Address}
\n";
//模式
$patterns =array(
"/{Address}/",
"/{Name}/",
"/{Email}/"
);
//替换字串
$replacements = array (
"No.5, Wilson St., New York, U.S.A",
"Thomas Ching",
"tom@emailaddress.com",
);
//输出模式替换结果
print preg_replace($patterns, $replacements, $string);
?>
输出结果如下。
Name: Thomas Ching",
Email: tom@emailaddress.com
Address: No.5, Wilson St., New York, U.S.A
在preg_replace的正则表达式中可以使用模式修正符“e”。其作用是将匹配结果用作表达式,并且可以进行重新运算。例如:
$html_body = “<HTML>