はじめに
PHPで正規表現を使った文字列置換を行う際、多くの開発者はpreg_replace
を使いますが、実はpreg_filterという便利な関数があることをご存知でしょうか?
この2つの関数は非常に似ていますが、決定的な違いがあります。今回はpreg_filter
の使い方と、preg_replace
との違いを実例を交えて詳しく解説します。
preg_filter関数とは?
preg_filter
は、正規表現パターンによる検索と置換を行い、マッチした要素のみを返すPHP関数です。
基本構文
preg_filter(
string|array $pattern,
string|array $replacement,
string|array $subject,
int $limit = -1,
int &$count = null
): string|array|null
パラメータ:
$pattern
– 検索する正規表現パターン$replacement
– 置換後の文字列$subject
– 対象となる文字列または配列$limit
– 置換回数の上限(デフォルト: -1で無制限)$count
– 置換が行われた回数が格納される(参照渡し)
戻り値:
- マッチして置換された要素のみの文字列または配列
- マッチしない場合は空配列または
null
preg_replaceとの決定的な違い
最大の違い:マッチしなかった要素の扱い
これがpreg_filter
とpreg_replace
の最も重要な違いです。
<?php
$subjects = [
'apple',
'banana',
'cherry',
'date',
'elderberry'
];
$pattern = '/^[ae]/i'; // aまたはeで始まる単語
$replacement = 'FRUIT: $0';
// preg_replaceの場合
$result_replace = preg_replace($pattern, $replacement, $subjects);
print_r($result_replace);
/* 出力:
Array
(
[0] => FRUIT: apple
[1] => banana // マッチしなくても残る
[2] => cherry // マッチしなくても残る
[3] => date // マッチしなくても残る
[4] => FRUIT: elderberry
)
*/
// preg_filterの場合
$result_filter = preg_filter($pattern, $replacement, $subjects);
print_r($result_filter);
/* 出力:
Array
(
[0] => FRUIT: apple
[4] => FRUIT: elderberry
)
// マッチしなかった要素は除外される!
*/
重要ポイント:
preg_replace
: マッチしない要素もそのまま返すpreg_filter
: マッチした要素のみを返す(フィルタリング機能)
実践的な使用例
1. メールアドレスのフィルタリングと整形
<?php
$contacts = [
'user@example.com',
'電話番号: 090-1234-5678',
'admin@test.jp',
'住所: 東京都',
'support@company.co.jp'
];
// メールアドレスだけを抽出して整形
$pattern = '/^([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/';
$replacement = 'Email: $1';
$emails = preg_filter($pattern, $replacement, $contacts);
print_r($emails);
/* 出力:
Array
(
[0] => Email: user@example.com
[2] => Email: admin@test.jp
[4] => Email: support@company.co.jp
)
*/
2. URLリストのクリーンアップ
<?php
$urls = [
'https://example.com/page1',
'invalid-url',
'http://test.com',
'not a url at all',
'https://secure-site.jp/path/to/resource'
];
// 有効なURLだけを抽出してプロトコルを統一
$pattern = '/^(https?:\/\/.+)$/';
$replacement = '$1';
$validUrls = preg_filter($pattern, $replacement, $urls);
print_r($validUrls);
/* 出力:
Array
(
[0] => https://example.com/page1
[2] => http://test.com
[4] => https://secure-site.jp/path/to/resource
)
*/
3. 数値のみを抽出して計算用に変換
<?php
$data = [
'価格: 1,200円',
'在庫: 45個',
'キャンペーン実施中',
'割引: 300円',
'送料無料'
];
// 数値を含む項目だけを抽出して数値部分を取り出す
$pattern = '/(\d+(?:,\d+)*)/';
$replacement = '$1';
$numbers = preg_filter($pattern, $replacement, $data);
print_r($numbers);
/* 出力:
Array
(
[0] => 1,200
[1] => 45
[3] => 300
)
*/
4. ログファイルからエラーメッセージだけを抽出
<?php
$logLines = [
'[INFO] Application started',
'[ERROR] Database connection failed',
'[DEBUG] Query executed in 0.5ms',
'[ERROR] File not found: config.php',
'[INFO] User logged in',
'[WARNING] Memory usage high'
];
// ERRORレベルのログだけを抽出して整形
$pattern = '/^\[ERROR\]\s*(.+)$/';
$replacement = '🔴 Error: $1';
$errors = preg_filter($pattern, $replacement, $logLines);
print_r($errors);
/* 出力:
Array
(
[1] => 🔴 Error: Database connection failed
[3] => 🔴 Error: File not found: config.php
)
*/
5. HTMLタグを含む行だけを処理
<?php
$content = [
'プレーンテキスト',
'<p>段落テキスト</p>',
'別のプレーンテキスト',
'<div class="container">コンテナ</div>',
'<span>スパン要素</span>'
];
// HTMLタグを含む行だけを抽出してタグを除去
$pattern = '/<[^>]+>(.+)<\/[^>]+>/';
$replacement = 'HTML: $1';
$htmlContent = preg_filter($pattern, $replacement, $content);
print_r($htmlContent);
/* 出力:
Array
(
[1] => HTML: 段落テキスト
[3] => HTML: コンテナ
[4] => HTML: スパン要素
)
*/
複数パターンの使用例
preg_filter
は複数のパターンと置換を配列で指定できます。
<?php
$texts = [
'call me at 090-1234-5678',
'email: test@example.com',
'visit http://example.com',
'just plain text',
'phone: 080-9876-5432'
];
// 複数のパターンで異なる処理
$patterns = [
'/\d{3}-\d{4}-\d{4}/', // 電話番号
'/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i', // メール
'/https?:\/\/[^\s]+/' // URL
];
$replacements = [
'[PHONE: $0]',
'[EMAIL: $0]',
'[URL: $0]'
];
$filtered = preg_filter($patterns, $replacements, $texts);
print_r($filtered);
/* 出力:
Array
(
[0] => call me at [PHONE: 090-1234-5678]
[1] => email: [EMAIL: test@example.com]
[2] => visit [URL: http://example.com]
[4] => phone: [PHONE: 080-9876-5432]
)
*/
パフォーマンスの考慮
いつpreg_filterを使うべきか?
preg_filterが適している場合:
- 大量のデータから条件に合うものだけを抽出したい
- マッチしないデータは不要
- メモリ効率を重視したい
preg_replaceが適している場合:
- すべての要素を保持する必要がある
- マッチしない要素もそのまま残したい
- 全体的な文字列変換が目的
<?php
// 10000件のデータから特定条件のみ抽出する場合
$largeDataset = array_fill(0, 10000, 'various data...');
// preg_filterの方が効率的
$filtered = preg_filter('/^specific_pattern/', 'replaced', $largeDataset);
// 結果は条件に合う要素のみ → メモリ節約
// preg_replaceは全要素を返す
$replaced = preg_replace('/^specific_pattern/', 'replaced', $largeDataset);
// 結果は10000件すべて → メモリ消費大
エラーハンドリング
<?php
function safePregrFilter($pattern, $replacement, $subject) {
$result = @preg_filter($pattern, $replacement, $subject);
if (preg_last_error() !== PREG_NO_ERROR) {
$errors = [
PREG_INTERNAL_ERROR => '内部エラー',
PREG_BACKTRACK_LIMIT_ERROR => 'バックトラック制限超過',
PREG_RECURSION_LIMIT_ERROR => '再帰制限超過',
PREG_BAD_UTF8_ERROR => '不正なUTF-8',
PREG_BAD_UTF8_OFFSET_ERROR => '不正なUTF-8オフセット'
];
$errorMsg = $errors[preg_last_error()] ?? '不明なエラー';
throw new Exception("preg_filterエラー: {$errorMsg}");
}
return $result;
}
// 使用例
try {
$result = safePregrFilter('/pattern/', 'replacement', $subjects);
} catch (Exception $e) {
error_log($e->getMessage());
}
よくある使用パターン
キーを保持したフィルタリング
<?php
$users = [
'user1' => 'admin@example.com',
'user2' => 'invalid email',
'user3' => 'user@test.com',
'user4' => 'not an email'
];
$pattern = '/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i';
$validEmails = preg_filter($pattern, '$0', $users);
print_r($validEmails);
/* 出力:
Array
(
[user1] => admin@example.com
[user3] => user@test.com
)
// キーが保持される!
*/
まとめ
preg_filter
は、正規表現によるフィルタリングと置換を同時に行える強力な関数です。
覚えておくべきポイント:
- preg_replaceとの違い: マッチした要素のみを返す
- 用途: データのフィルタリングと変換を同時に行いたい場合に最適
- メモリ効率: 不要なデータを除外するため、大量データ処理で有利
- 配列のキー: 元の配列のキーは保持される
使い分けの基準:
- 全要素が必要 →
preg_replace
- マッチした要素のみ必要 →
preg_filter
この関数を使いこなせば、データのフィルタリングと整形を一度に効率よく行えます。ぜひ実際のプロジェクトで活用してみてください!