これは何?
データベースの LIKE 検索用途とかでキーワードを抽出するやつ。
例えば
PHP Qiita Laravel
と入力したら,
PHP
Qiita
Laravel
として取り出す関数を作ります。
実装
実装したい関数の形を示し, 2 つの異なるアプローチでの実装例を記載します。
関数シグネチャ
$input
に入力文字列を渡し, $limit
にキーワード数制限を指定します。 -1
を指定した場合は無制限になり,これをデフォルトとします。
function extractKeywords(string $input, int $limit = -1): array
手法の比較
※ 「手法」の名称は一般的に提唱されているものではありません
手法 | 説明 | よく使われる関数 |
---|---|---|
空白分割法 | 空白文字で分割し,残った部分をキーワードとする。キーワード数制限を超過する場合,最後の1つの中に空白文字を含む残りのすべてが含まれる。 |
preg_split() explode()
|
非空白抽出法 | 空白文字ではない部分だけを直接キーワードとして抽出する。キーワード数制限を超過する場合,単に超過分は無視される。 |
preg_match_all() preg_replace_callback()
|
実装が簡単なのは空白分割法のほうですが,キーワード数制限超過時の挙動がそれぞれ異なるので注意してください。例えば a b c d e
において $limit = 3
で抽出する場合,
- 空白分割法の場合,
a
b
c d e
となる - 非空白抽出法の場合,
a
b
c
となる
という違いがあります。
空白分割法
半角スペースで分割
一番シンプルな実装。 explode()
を使う手法もありますが, $limit
の適用や空文字列の除外を考えると,こちらのほうが基本的に上位互換です。
function extractKeywords(string $input, int $limit = -1): array
{
return preg_split('/ ++/', $input, $limit, PREG_SPLIT_NO_EMPTY);
}
あらゆる空白文字で分割
半角スペースの他に全角スペース,改行,タブ,ノーブレークスペースなどあらゆる空白系の制御文字を対象とする場合はこちら。 \p{Z}
は ASCII 範囲にある制御文字の集合, \p{Cc}
は Unicode 範囲にある制御文字の集合を表しています。またこれらを適用するためには u
フラグが必須になります。
function extractKeywords(string $input, int $limit = -1): array
{
return preg_split('/[\p{Z}\p{Cc}]++/u', $input, $limit, PREG_SPLIT_NO_EMPTY);
}
非空白抽出法
半角スペース以外を抽出
function extractKeywords(string $input, int $limit = -1): array
{
$matches = [];
preg_replace_callback(
'/[^ ]++/',
function (array $match) use (&$matches) {
$matches[] = $match[0];
},
$input,
$limit,
$_,
PREG_SET_ORDER
);
return $matches;
}
「なんで preg_match_all()
じゃないんだ!?」
が素直な感想だと思います。 preg_replace_callback()
で参照代入を使うみたいな変なことをやっているのは, $limit
適用に対応するためです。 preg_match_all()
にはマッチング回数を制限する仕組みがありません。
あらゆる空白文字以外を抽出
function extractKeywords(string $input, int $limit = -1): array
{
$matches = [];
preg_replace_callback(
'/[^\p{Z}\p{Cc}]++/u',
function (array $match) use (&$matches) {
$matches[] = $match[0];
},
$input,
$limit,
$_,
PREG_SET_ORDER
);
return $matches;
}
パターンの部分を変えるだけで全体的な体裁は同じですね。
ダブルクオーテーションで括った部分は保持したまま,あらゆる空白文字以外の部分を抽出
こちらは,非空白抽出法でしか実装できません。
Laravel "Taylor Otwell" PHP
と入力したら Laravel
Taylor Otwell
PHP
として欲しい場合はこちら。 厳密にイコールではないですが,ある程度 Google 検索っぽい動きになります。
function extractKeywords(string $input, int $limit = -1): array
{
$matches = [];
preg_replace_callback(
'/""(*SKIP)(*FAIL)|"([^"]++)"|([^"\p{Z}\p{Cc}]++)/u',
function (array $match) use (&$matches) {
$matches[] = $match[2] ?? $match[1];
},
$input,
$limit,
$_,
PREG_SET_ORDER
);
return $matches;
}
ダブルクオーテーションで括られた空文字列は, PCRE 特有の機能である (*SKIP)(*FAIL)
でマッチングを強制終了して $limit
適用の際に 1 キーワードとしてカウントしない,など少し気を利かせています。
補足
重複を削除する
もし重複したキーワードを削除する場合は, array_unique()
+ array_values()
の処理を組み合わせてください。以下に使用率の高そうな,適用版の一部を記載しておきます。
function extractKeywords(string $input, int $limit = -1): array
{
return array_values(array_unique(preg_split('/[\p{Z}\p{Cc}]++/u', $input, $limit, PREG_SPLIT_NO_EMPTY)));
}
function extractKeywords(string $input, int $limit = -1): array
{
$matches = [];
preg_replace_callback(
'/""(*SKIP)(*FAIL)|"([^"]++)"|([^"\p{Z}\p{Cc}]++)/u',
function (array $match) use (&$matches) {
$matches[] = $match[2] ?? $match[1];
},
$input,
$limit,
$_,
PREG_SET_ORDER
);
return array_values(array_unique($matches));
}