導入
PHPで文字列操作を行う際、mb_strpos関数は頻繁に使用されますが、この関数は「最初に見つかった位置」しか返しません。実務では、ログ解析や文章の校正ツール、キーワード抽出機能などにおいて「文字列内のすべての出現位置」を特定したいケースが多々あります。本記事では、この課題を解決するための効率的な実装方法を解説します。
基礎知識
PHPにおける文字列検索の基本は、マルチバイト対応のmb_strpos関数です。この関数は、対象文字列(haystack)からキーワード(needle)を検索し、最初に見つかった文字位置を整数で返します。見つからない場合はfalseを返します。
「すべて取得する」ためには、見つかった位置の次から再び検索を繰り返すというロジックが必要になります。
実装/解決策
実装には「再帰処理」を用いる方法と「ループ処理」を用いる方法の2通りがあります。実務の現場では、スタックオーバーフローのリスクを避け、メモリ効率と可読性の観点からwhile文によるループ処理を推奨します。オフセット(検索開始位置)を更新しながら検索を続けることで、すべての出現インデックスを配列として取得できます。
サンプルプログラム
以下のコードは、while文を使用して文字列内のすべてのキーワード位置を取得する実用的な例です。
/
function get_all_positions(string $haystack, string $needle): array
{
$offset = 0;
$result = [];
// マルチバイト対応のため mb_strpos を使用
while (($pos = mb_strpos($haystack, $needle, $offset)) !== false) {
$result[] = $pos;
// 次の検索開始位置を、現在の位置の次の文字に設定
// 重なりを考慮しない場合は $pos + mb_strlen($needle) に変更してください
$offset = $pos + 1;
}
return $result;
}
// 実行例
$text = ‘PHPは便利です。PHPは強力な言語です。PHPはWeb開発の要です。’;
$keyword = ‘PHP’;
$positions = get_all_positions($text, $keyword);
// 結果の出力
echo “キーワード ‘{$keyword}’ の出現位置: ” . implode(‘, ‘, $positions) . ” 文字目” . PHP_EOL;
// 出力: キーワード ‘PHP’ の出現位置: 0, 8, 19 文字目
?>
応用・注意点
1. 重なりについて
上記のサンプルは $pos + 1 としているため、キーワード自体が重なっている場合(例:’aaaa’から’aa’を検索)も漏れなく取得できます。もし重複を許さず、見つかった単語の直後から検索を再開したい場合は、$offset = $pos + mb_strlen($needle) と書き換えてください。
2. パフォーマンス
非常に巨大な文字列(数MBを超えるようなログデータ等)に対してこの処理を行う場合は、メモリ消費量に注意が必要です。必要に応じて、文字列全体をメモリに載せず、ファイルポインタを使用してストリーム処理を行うことも検討してください。
3. エンコーディング
mb_strposを使用する際は、必ずPHPの設定(mb_internal_encoding)が検索対象の文字列と一致していることを確認してください。環境によって文字化けや予期せぬ挙動を引き起こす原因となります。