PHP Tips | 文字列処理:検索・置換 – 単語境界のみ置換

PHP PHP
スポンサーリンク

「単語境界のみ置換」って何をしたいのか

まず、やりたいことのイメージからいきます。

「単語境界のみ置換」は、こういうニーズです。

cat という単語だけを dog に置き換えたい。
でも、category の中の cat までは置き換えたくない。」

具体的にはこうです。

元の文字列:
  "I have a cat. This category is about cats."

やりたいこと:
  "cat" という「単語」だけを "dog" に置き換えたい。

結果:
  "I have a dog. This category is about cats."

ここで重要なのは、

  • catdog に置き換えたい
  • でも category の中の cat はそのままにしたい

という「単語としての境界」を意識した置換です。

普通の str_replace('cat', 'dog', ...) を使うと、
category の中の cat まで全部置き換わってしまいます。

そこで出てくるのが「単語境界」を扱える正規表現置換です。


なぜ str_replace ではダメなのか

str_replace は「部分一致」さえしていれば、容赦なく置き換えます。

$text = "I have a cat. This category is about cats.";

$result = str_replace("cat", "dog", $text);

echo $result;
// I have a dog. This dogegory is about dogs.
PHP

見ての通り、

  • catdog
  • category の中の catdogegory
  • cats の中の catdogs

と、「単語としての cat」だけでなく、
他の単語の一部まで全部変わってしまいます。

「単語としての cat だけを置き換えたい」場合には、
str_replace では制御できません。

ここで「単語境界」を理解している正規表現の出番になります。


正規表現の「単語境界」\b を使う

\b の意味

正規表現には \b という「単語境界」を表すメタ文字があります。

ざっくり言うと、

「単語文字(英数字やアンダースコア)と、それ以外の境目」

の位置にマッチします。

例えば、"cat" という単語に対して、

  • " cat " の前後
  • "cat." の後ろ
  • "cat," の後ろ

など、「単語として区切られている位置」に \b がマッチします。

逆に、"category" の中の cat の前後は、
どちらも「単語文字同士」なので、そこには \b はありません。

この性質を使って、

「単語としての cat だけにマッチするパターン」

を作ります。

パターン:\bcat\b

$text = "I have a cat. This category is about cats.";

$result = preg_replace('/\bcat\b/', 'dog', $text);

echo $result;
// I have a dog. This category is about cats.
PHP

パターン /\bcat\b/ を分解すると、

  • \b:単語境界
  • cat:文字列 “cat”
  • \b:単語境界

つまり、「前後が単語境界になっている cat」だけにマッチします。

その結果、

  • " cat." の中の cat → 前後が境界なのでマッチ → dog に置換
  • "category" の中の cat → 前後が単語文字なのでマッチしない → そのまま
  • "cats" の中の cat → 後ろが s(単語文字)なのでマッチしない → そのまま

となります。

これが「単語境界のみ置換」の基本パターンです。


大文字小文字を無視して単語境界置換する

“Cat” も “CAT” もまとめて置換したい

英語の文章では、先頭だけ大文字になっていることも多いです。

$text = "Cat and cat and CAT are all here.";
PHP

これに対して、「単語としての cat を全部 dog にしたい」なら、
大文字小文字も無視したいですよね。

その場合は、正規表現の i フラグ(ignore case)を使います。

$text = "Cat and cat and CAT are all here.";

$result = preg_replace('/\bcat\b/i', 'dog', $text);

echo $result;
// dog and dog and dog are all here.
PHP

/...\b/ii が「大文字小文字を無視する」指定です。

ここでの重要ポイントは、

「単語境界」と「大文字小文字無視」を組み合わせると、
単語としての cat を、表記ゆれごと一気に置換できる

ということです。


実務での使いどころ

NGワードを「単語として」だけマスクしたい

例えば、NGワード "bad" をマスクしたいとします。

$text = "This is a bad example of a badge.";
PHP

str_replace("bad", "***", ...) を使うと、

// This is a *** example of a ***ge.
PHP

となり、badge の中の bad までマスクされてしまいます。

単語としての "bad" だけをマスクしたいなら、こうします。

$result = preg_replace('/\bbad\b/i', '***', $text);

echo $result;
// This is a *** example of a badge.
PHP

これなら、"bad" だけがマスクされ、"badge" はそのままです。

コマンドやキーワードだけを置換したい

ログやスクリプトの中で、特定のコマンド名だけを置換したいときにも使えます。

$text = "run task1; rerun task1; task10 is different.";

$result = preg_replace('/\btask1\b/', 'taskX', $text);

echo $result;
// run taskX; rerun taskX; task10 is different.
PHP

task10 の中の task1 は単語境界ではないので、そのまま残ります。


ユーティリティ関数としてまとめる

単語境界置換のラッパー

毎回パターンを書くのが面倒なら、
「単語としての置換」をするユーティリティ関数にしてしまうと便利です。

/**
 * 単語境界のみ置換(大文字小文字を区別する)
 */
function replaceWord(string $word, string $replacement, string $text): string
{
    $pattern = '/\b' . preg_quote($word, '/') . '\b/';

    return preg_replace($pattern, $replacement, $text);
}
PHP

ここでの preg_quote($word, '/') は、
$word の中に .+ などの正規表現の記号が入っていても、
「ただの文字」として扱えるようにエスケープしてくれます。

使い方はこうです。

$text = "I have a cat. This category is about cats.";

echo replaceWord('cat', 'dog', $text);
// I have a dog. This category is about cats.
PHP

大文字小文字を無視する版

/**
 * 単語境界のみ置換(大文字小文字を無視)
 */
function replaceWordIgnoreCase(string $word, string $replacement, string $text): string
{
    $pattern = '/\b' . preg_quote($word, '/') . '\b/i';

    return preg_replace($pattern, $replacement, $text);
}
PHP
$text = "Cat and cat and CAT are all here.";

echo replaceWordIgnoreCase('cat', 'dog', $text);
// dog and dog and dog are all here.
PHP

「単語としての置換」を関数名にしておくことで、
呼び出し側のコードを読んだときに意図がすぐ伝わります。


日本語と「単語境界」の注意点

ここは少しだけ重要な補足です。

\b の「単語境界」は、
基本的に「英数字とアンダースコア」を「単語文字」として扱います。

日本語(ひらがな・カタカナ・漢字)は、
\b の対象外になることが多いです。

つまり、

$text = "ねこ と 猫cat がいます。";

preg_replace('/\bcat\b/', 'dog', $text);
PHP

のような場合は、cat の前後が英数字ではないので、
\b の挙動が直感とズレることがあります。

日本語を含むテキストで「単語境界」を厳密に扱いたい場合は、

  • スペースや句読点で区切る
  • 自分で「境界条件」を正規表現で書く(例:(^|[\s、。])cat($|[\s、。])

など、もう一段工夫が必要になります。

英字だけの単語を対象にするなら、\b で十分です。


まとめ:今日からの「単語境界のみ置換」ユーティリティ

押さえておきたいポイントをコンパクトにまとめます。

単語境界のみ置換は、

  • str_replace だと「単語の一部」まで置き換わってしまう問題を避けるためのテクニック。
  • 正規表現の \b(単語境界)を使って、/\bword\b/ のようなパターンで「単語としての word」だけにマッチさせる。
  • 大文字小文字を無視したいときは、/\bword\b/i のように i フラグを付ける。
  • 実務では、NGワードマスク、コマンド名の置換、英単語の表記ゆれ修正などでよく使える。

タイトルとURLをコピーしました