10週間ウェブ開発講座

裏メニュー第五週

正規表現

PHPで正規表現を使用する方法について説明します。

ファイル取得関数

PHPにはファイルを操作するための関数が多数存在します。
詳細についてはPHPマニュアルの「ファイルシステム 関数」に譲ります。
ここではfile_get_contents関数についてのみ扱います。

file_get_contentsは第一引数としてファイル名を受取り、ファイルの中身を返り値とする関数です。
簡単なサンプルを示します。

<?php

$string = file_get_contents('/home/y-mahata/htdocs/pukiwiki/README.txt');
echo $string;

?>

実行結果は次のようになります。

名前
    PukiWiki - 自由にページを追加・削除・編集できるWebページ構築スクリプト

    Version 1.4.7
    Copyright (C)
      2001-2006 PukiWiki Developers Team
      2001-2002 yu-ji (Based on PukiWiki 1.3 by yu-ji)
    License: GPL version 2 or (at your option) any later version
(略)

このプログラムは、s-tanno.comのファイル(/home/y-mahata/htdocs/pukiwiki/README.txt)を読込み、
その内容を出力します。

また、file_get_contentsは第一引数にURLを渡すこともできます。
次のプログラムを見てみましょう。

<?php

$string = file_get_contents('http://y-mahata.s-tanno.com/pukiwiki/');
echo $string;

?>

実行結果は次のようになります。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
 <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
 <meta http-equiv="content-style-type" content="text/css" />

 <title>FrontPage - PukiWiki</title>
(略)

このプログラムは、 http://y-mahata.s-tanno.com/pukiwiki/ を読込み、
その内容を出力します。

文字コード変換関数

PHPにはマルチバイト文字列を操作するための関数が多数存在します。
詳細についてはPHPマニュアルの「マルチバイト文字列 関数」に譲ります。
ここではmb_convert_encoding関数についてのみ扱います。

mb_convert_encodingは第一引数として文字列を、第二引数として変換先文字コードを、第三引数として変換元文字コードを受取り、 文字コードを変換した後の文字列を返り値とする関数です。

第二引数や第三引数として指定できる値(文字コード)には次のようなものがあります。

  • 'UTF-8'
  • 'EUC-JP'
  • 'SJIS'
  • 'ASCII'
  • 'JIS'
  • 'auto'

最後の'auto'は文字コードの判別を「自動で行いたい」ときに第三引数に指定します。

簡単なサンプルを示します。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS" />
</head>

<body>
<?php
     echo mb_convert_encoding('UTF-8からShift-JISに変換された文字列です。', 'SJIS', 'UTF-8');
?>
</body>
</html>

metaタグ内でShift-JISのページであると指定しているにも関わらず、文字化けが発生しないことを確認してください。

PCRE関数

PHPには正規表現を利用するための関数が多数存在します。
それらを大きく分類すると「POSIX正規表現関数」と「PCRE関数」です。
POSIX正規表現関数はPHP6で削除される予定なので、ここではPCRE関数についてのみ扱います。

PCRE関数を学ぶ前に、正規表現を次のように定義しましょう。

コンピューターで文字列の検索や置換を行う時に用いられる表記法。
通常の文字のほかに、メタキャラクターという特殊の意味を持つ記号を組み合わせることにより、
特定の文字列のパターンを検索・抽出・置換することができる。
(正規表現 とは - コトバンク)

この定義からわかるように、正規表現を利用するためには
「通常の文字とメタキャラクター*1を組合せて特定の文字列とマッチするパターン」を作れなければなりません。

例としてURL中からドメイン部分を抽出するプログラムを示します。

  1. <?php
  2.  
  3. preg_match('@^http://([^/]+)@i', 'http://www.php.net/index.html', $matches);
  4. echo $matches[1]; // www.php.net
  5.  
  6. ?>

現時点でこのプログラムの詳細が掴めなくても問題ありません。 preg_match関数の第一引数に'@^http://([^/]+)@i'という不思議な文字列が渡っていることだけ確認してください。 この'@^http://([^/]+)@i'が「パターン」と呼ばれるものであり、
パターンを読み書きできるようになることが今週の目的です。

メタ文字

メタ文字の中でもよく使われるものについて取り上げます。

メタ文字には二種類あり、一つは「角カッコ内を除き、パターン中のどこででも使用できる文字」で、もう一つは「角カッコで括られた中でだけ使用できる文字」です。

「角カッコ内を除き、パターン中のどこででも使用できる文字」には次のようなものがあります。

記号説明
\多目的に使う一般的なエスケープ文字
^検索対象(複数行モードでは行)の始まりを言明
$検索対象(複数行モードでは行)の終わりを言明
.改行を除くすべての文字にマッチ(デフォルト時)
(サブパターンの開始
)サブパターンの終了
?0または1回マッチ/なるべく少ない回数だけマッチ
*0回以上の繰り返し
+1回以上の繰り返し
[文字クラス定義の開始
]文字クラス定義の終了

「角カッコで括られた中でだけ使用できる文字」には次のようなものがあります。

記号説明
\一般的なエスケープ文字
^クラスの否定。ただし、文字クラスの最初の文字に用いた場合のみ
-文字の範囲の指定

特に注意すべきなのは ^ の意味が出現位置によって異なる点です。
不慣れな内は正規表現中で ^ が存在するときに、それがどのような意味を持つのかに注意してください。

パターン修飾子

パターン修飾子の中でもよく使われるものについて取り上げます。

記号説明
iこの修飾子を設定すると、パターンの中の文字は大文字にも小文字にもマッチします。
sこの修飾子を設定すると、パターン中のドットメタ文字は改行を含む全ての文字にマッチします。
Uこの修飾子を設定すると、量指定子の「貪欲さ」が反転します。

正規表現が「貪欲である」とは「できるだけ長いの文字列にマッチする」という意味です。

メタ文字とパターン修飾子を組合せたサンプル

次のプログラムを実行し、正規表現の動作を確かめてみましょう。

<?php

function NumCheck($text) {
    if (1 === preg_match('/^[0-9]+$/', $text)) {
        echo "'{$text}'は数字列です。\n";
    } else {
        echo "'{$text}'は数字列ではありません。\n";
    }
}

function StringCheck($text) {
    // if (1 === preg_match('/^[a-z]+$/i', $text)) { // これでも可
    if (1 === preg_match('/^[a-zA-Z ]+$/', $text)) {
        echo "'{$text}' は英文字列です。\n";
    } else {
        echo "'{$text}' は英文字列ではありません。\n";
    }
}

function TemperatureCheck($text) {
    if (1 === preg_match('/^([+-]?[0-9]+)([CF])$/', $text, $matches)) {
        if ('C' === $matches[2]) {
            echo "'{$text}' はセ氏{$matches[1]}度です。\n";
        } else {
            echo "'{$text}' は華氏{$matches[1]}度です。\n";
        }
    } else {
        echo "'{$text}' は温度ではありません。\n";
    }
}

// 厳密な電子メールアドレスのチェックにはなっていません
// ref) http://blog.livedoor.jp/dankogai/archives/51189905.html
// ref) "(?:" について http://jp.php.net/manual/ja/regexp.reference.subpatterns.php
function MailCheck($text) {
    if (1 === preg_match('/^[a-zA-Z0-9_-]+\@[a-zA-Z0-9_-]+(?:\.\w+)+$/', $text, $matches)) {
        echo "'{$text}' は(たぶん)電子メールアドレスです(...なんじゃないかなあ)。\n";
    } else {
        echo "'{$text}' は(たぶん)電子メールアドレスではありません(...だと思うなあ)。\n";
    }
}

// 厳密なURLのチェックにはなっていません
function UrlCheck($text) {
    if (1 === preg_match('@^(?:http://)([^/]+)@i', $text, $matches)) {
        echo "'{$text}' は(たぶん)URLです(...なんじゃないかなあ)。ドメインは'{$matches[1]}'だと思います。\n";
    } else {
        echo "'{$text}' は(たぶん)URLではありません(...だと思うなあ)。\n";
    }
}

$strings = array('0123456789', // 単純な数字列
                 'this is a sentence', // 単純な英文字列
                 'i have 30 dollars in my wallet', // 数字と文字の組合せ
                 '-35C', // 南極の気温
                 'foobar@example.co.jp', // 電子メールアドレス
                 'http://www.example.com/foo/bar.html' // URL
           );

foreach ($strings as $string) {
    NumCheck($string);
    StringCheck($string);
    TemperatureCheck($string);
    MailCheck($string);
    UrlCheck($string);
    echo "\n";
}

?>

また、貪欲な正規表現の振舞いを理解するために、次のプログラムを実行し、結果を考察しましょう。

<?php

$text = 'The author of "Wicked" also wrote "Mirror, Mirror."';
if (preg_match('/".+"/', $text, $matches)) {
    echo "$matches[0]\n";
}

if (preg_match('/".+"/U', $text, $matches)) {
    echo "$matches[0]\n";
}

?>

宿題

正規表現に習熟するためには、多くの正規表現の読み書きをこなすしかありません。
ここで宿題として列挙するものは非常に限定的なものです。
今後、プログラミングで文字列を操作するときには積極的に正規表現を利用してみてください。 また、他人が書いた正規表現をよく読むようにしてみてください(色々とツッコミを入れたくなることもあるでしょう)。

  • 携帯電話の電話番号が「090か080で始まり xxx-xxxx-xxxx (x: 0以上の任意の整数) という形式である」と仮定し、入力された文字列が有効な携帯電話の電話番号かどうかを検証する関数を書いてください。
  • http://www.yahoo.co.jp/ http://www.google.co.jp/ http://mixi.jp/ http://ecnavi.jp/ からtitleタグに指定されている文字列を取得するプログラムを書いてください(取得した文字列はutf-8で出力してください)。
  • http://ecnavi.jp/ から全てのaタグのhref属性を取得するプログラムを書いてください。
  • (オプション)検索エンジンを活用し、多くの人が電子メールアドレスを正規表現で扱うことに苦労している様を眺めてください。
  • (オプション)検索エンジンを活用し、多くの人がURLを正規表現で扱うことに苦労している様を眺めてください。

参考資料


*1 以降では、PHPマニュアルと整合性を取るために「メタ文字」と表現します。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-06-10 (木) 22:07:22 (87d)