🔌

ChatGPTの回答がきっかけでpreg_replaceの仕様を調べた話

2024/03/25に公開

こんにちは〜!
NE株式会社のはやしまき(@_mkmk884)です🦒

みなさまコードを書く際にChatGPTやCopilotを使っていますでしょうか?
私は文字列から特定の文字列を前方一致で削除した結果を取得する処理を書く際、ChatGPTに質問しました。
preg_replaceを用いた2種類のコードがChatGPTから返ってきたので、違いを知るためにpreg_replaceの仕様を調べました!

preg_replaceを用いて前方一致にさせるコードの書き方2種類とpreg_replaceの第四引数の仕様を、このブログに残します💪

やりたいこと

文字列$completeItemCodeから特定の文字列$itemCodeを前方一致で削除し、残りの部分を抽出する処理です。

$completeItemCode = 'abc-def-ghi';
$itemCode = 'abc';

$branchNumber = '-def-ghi'; // これがほしい

ChatGPTが出してくれた前方一致で文字列置換する2種類のコード

  1. 正規表現を使う書き方
  2. 第四引数を使う書き方

の2種類をpreg_replaceを用いて回答してくれました。

正規表現を使う書き方

正規表現を使う書き方
<?php
$data1 = ["itemCode" => "aaa", "completeItemCode" => "aaa-b-c"];
$data2 = ["itemCode" => "aaa", "completeItemCode" => "aaa-aaa-c"];
$data3 = ["itemCode" => "item", "completeItemCode" => "itembbbbc"];

function extractBranchNumber($data) {
    // "itemCode" を "completeItemCode" から削除し、残りの部分を取得
    $branchNumber = preg_replace('/^' . preg_quote($data["itemCode"], '/') . '/', '', $data["completeItemCode"]);

    return $branchNumber;
}

// テスト
$output1 = extractBranchNumber($data1);
$output2 = extractBranchNumber($data2);
$output3 = extractBranchNumber($data3);

echo $output1 . "\n";  // 出力: -b-c
echo $output2 . "\n";  // 出力: -aaa-c
echo $output3 . "\n";  // 出力: bbbbc
?>

ChatGPTが提示してくれたもののまま記載しています。

前方一致で文字列置換している部分を抜き出したもの(変数は見やすいように変更)
$branchNumber = preg_replace('/^' . preg_quote($itemCode, '/') . '/', '', $completeItemCode);

正規表現のメタ文字^を用いて、行の先頭にマッチしたもののみ置換します。

第四引数を使う書き方

第四引数を使う書き方
<?php

function extractBranchFromCompleteItemCode($itemCode, $completeItemCode) {
    // itemCodeがcompleteItemCode内で頭文字に見つかった場合
    if (strpos($completeItemCode, $itemCode) === 0) {
        // preg_replaceを使用して最初に見つかったitemCodeのみ削除
        $branch = preg_replace("/$itemCode/", '', $completeItemCode, 1);
        
        // 結果を返す
        return $branch;
    }
    
    // インデックスが見つからない場合はエラーまたはデフォルト値を返す
    return "Error: itemCode not found in completeItemCode";
}

// テストケース
$itemCode1 = "aaa";
$completeItemCode1 = "aaa-b-c";
$result1 = extractBranchFromCompleteItemCode($itemCode1, $completeItemCode1);
echo $result1 . PHP_EOL;  // 出力: -b-c

$itemCode2 = "aaa";
$completeItemCode2 = "aaa-aaa-c";
$result2 = extractBranchFromCompleteItemCode($itemCode2, $completeItemCode2);
echo $result2 . PHP_EOL;  // 出力: -aaa-c

$itemCode3 = "item";
$completeItemCode3 = "itembbbbc";
$result3 = extractBranchFromCompleteItemCode($itemCode3, $completeItemCode3);
echo $result3 . PHP_EOL;  // 出力: bbbbc

$itemCode4 = "item";
$completeItemCode4 = "itemitemitem";
$result4 = extractBranchFromCompleteItemCode($itemCode4, $completeItemCode4);
echo $result4 . PHP_EOL;  // 出力: itemitem
?>
前方一致で文字列置換している部分を抜き出したもの
    if (strpos($completeItemCode, $itemCode) === 0) {
        $branch = preg_replace("/$itemCode/", '', $completeItemCode, 1);
    }

preg_replaceの第四引数って何?と思ったので、公式ドキュメントを読みました🐘
PHP: preg_replace - Manual

limit
subject 文字列において、各パターンによる 置換を行う最大回数。デフォルトは -1 (制限無し)。

とあり、このlimitに1が入っていることにより、マッチしたら最大1回置換を行うということになります。

前方一致しているかどうかの判定は、if (strpos($completeItemCode, $itemCode) === 0)で行っており、$completeItemCodeの中で0番目(一番最初)に$itemCodeが現れているかどうかを判定しています。
PHP: strpos - Manual

(補足)実は…

実際、ChatGPTが回答してくれた第四引数を使う書き方は、以下のようにstrposで$completeItemCodeの中に$itemCodeが含まれているかどうかだけを判定しているものでした。

前方一致で判定できていないコード
    if (strpos($completeItemCode, $itemCode) !== false) {
        $branch = preg_replace("/$itemCode/", '', $completeItemCode, 1);
    }

上で書いている3、4種類のテストケースでは正しく前方一致を確認できません、、
以下のように、$completeItemCodeの文字列の途中に$itemCodeが入るパターンのテストケースが必要です。
普段はもちろんですが、生成AIに補佐してもらった際は特に意味のあるテストケースを用いてちゃんと確かめましょう😤

不足していた前方一致を確認するテストケース
$itemCode5 = "item";
$completeItemCode5 = "aaaitemaaa";  // 途中に挟む
$result5 = extractBranchFromCompleteItemCode($itemCode5, $completeItemCode5);
echo $result5 . PHP_EOL;

おわりに

標準関数のデフォルト値がある引数って使う機会が少なくないですか?
今回、公式ドキュメントを読んで「そんなパラメータがあったんだ〜」となりました。

簡単な処理でも生成AIに2回聞いてみると、異なる回答をくれたりして新たな発見があるのでオススメです!

NE株式会社の開発ブログ

Discussion