PHPでの正規表現によるコールバックを利用した置換

次の文字列があるとします:

php<?php
  $str = '2+3= 3+5= 7+8=';
?>

数値+数値= という形式のすべての構造を見つけ、等号の後に加算結果が表示されるようにする必要があるとします。

このようなタスクを解決するには、関数 preg_replace_callback を利用すると便利です。 この関数は、関数 preg_replace と同様に動作します(正規表現に基づいて検索と置換を行います)が、見つかった部分に対して追加の操作を実行することができます。

最初のパラメータとして関数は正規表現を受け取り、2番目は置換する文字列(またはコールバック)、3番目は置換が行われる文字列を受け取ります。

このとき、2番目のパラメータには、見つかった各一致に対して呼び出されるコールバック関数を渡す必要があります。

コールバックの最初のパラメータには、見つかった内容が格納される変数を指定する必要があります。 この変数は、見つかった一致のキャプチャグループ(括弧でキャプチャされた部分)の配列となります。

実際に試してみましょう。最初の数値を最初のキャプチャグループに、2番目の数値を2番目のキャプチャグループに格納しながら、部分文字列を取得するコードを書いてみます:

php<?php
  $str = '2+3= 3+5= 7+8=';
  
  preg_replace_callback('#(\d+)\+(\d+)=#', function($match) {
    var_dump($match);
  }, $str);
?>

その結果、var_dump が3回実行され、順番に以下が出力されます:

php['2+3=', '2', '3']
['3+5=', '3', '5']
['7+8=', '7', '7']

次に進みます。各一致は、コールバックが return を通じて返す内容に置き換えられます。 例として、検索対象の各部分文字列を記号 '!' に置き換えてみましょう:

php<?php
  $str = '2+3= 3+5= 7+8=';
  
  $res = preg_replace_callback('#(\d+)\+(\d+)=#', function($match) {
    return '!';
  }, $str);
  
  echo $res; // '! ! !' を出力します
?>

次に、各部分文字列をその中の数値の合計に置き換えてみましょう:

php<?php
  $str = '2+3= 3+5= 7+8=';
  
  $res = preg_replace_callback('#(\d+)\+(\d+)=#', function($match) {
    return $match[1] + $match[2];
  }, $str);
  
  echo $res; // '5 8 13' を出力します
?>

つまり、私たちのタスクはほぼ解決しました。 あとは、合計値の前に元の部分文字列が残るようにすれば完了です。そのためには、合計の前にゼロ番目のキャプチャグループの内容を挿入します:

php<?php
  $str = '2+3= 3+5= 7+8=';
  
  $res = preg_replace_callback('#(\d+)\+(\d+)=#', function($match) {
    return $match[0] . ($match[1] + $match[2]);
  }, $str);
  
  echo $res; // '2+3=5 3+5=8 7+8=13' を出力します
?>
Задание 1

以下の文字列が与えられます:

php<?php
  $str = 'The numbers are 3, 7 and 12';
?>

文字列内のすべての数値をその2乗に置き換えてください。

期待される結果:

php'The numbers are 9, 49 and 144'
Задание 2

以下の文字列が与えられます:

php<?php
  $str = '2023-01-15 2022-12-31 2024-05-20';
?>

すべての日付を 日.月.年 形式に変換してください。

Задание 3

以下の文字列が与えられます:

php<?php
  $str = 'Costs: $15, $20, $100';
?>

すべての価格を 10% 増やしてください。