質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.86%

  • PHP

    18610questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • 正規表現

    729questions

    正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

マルチバイト正規表現もどきを元にエレガントもしくはシンプルに文字列置換をしたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 275

taro_nii_chan

score 170

 質問

あらりうるえれおろかやゆよかまみきむめもく

という文字列を

あ+う○え*お+か+き+く

というマルチバイト正規表現もどきを元に

あ<span class='blue'>らり</span>う<span class='red'>る</span>え<span class='green'>れ</span>お<span class='blue'>ろかやゆよ</span>か<span class='blue'>まみ</span>き<span class='blue'>むめも</span>く

という文字列に変換するエレガントもしくはシンプルな方法を教えて下さい。

 詳細

マルチバイト正規表現もどきの説明です。
「○」と「*」は1文字を表しますが、「○」は1度だけ出てくるのに対し、「*」は0回以上出てきます。「+」は1文字以上の文字列を表します。0回以上出てきます。
「○」にヒットしたキーワードは赤、「*」にヒットしたキーワードは緑、「+」にヒットしたキーワードは青に表示させたいので、上記のspanタグのいっぱい入った文字列を作りたいのです。

 やってみた事

自分なりに書いたコードです。

<style>
.red { 
    color: #f00;
 }
 .green {
     color: #0c0;
 }
 .blue {
     color: #00f;
 }
</style>
<?php

$mb_pattern = "あ+う○え*お+*か+き+く";

$pattern = "/";
$j = 1;

for ($i = 0; $i < mb_strlen($mb_pattern); $i++) {
    $mb_char = mb_substr($mb_pattern, $i, 1);
    switch ($mb_char) {
        case '○':
            $pattern .= "(.)";
            $replacement .= "<span class='red'>$" . $j++ . "</span>";
            break;
        case '*':
            $pattern .= "(.)";
            $replacement .= "<span class='green'>$" . $j++ . "</span>";
            break;
        case '+':
            $pattern .= "(.+)";
            $replacement .= "<span class='blue'>$" . $j++ . "</span>";
            break;
        default:
            $pattern .= "(${mb_char})";
            $replacement .= "$" . $j++;
            break;
    }
}

$pattern .= "/u";

echo $pattern;
// /(あ)(.+)(う)(.)(え)(.)(お)(.+)(.)(か)(.+)(き)(.+)(く)/u

echo "<br>";
echo htmlentities($replacement);
// $1<span class='blue'>$2</span>$3<span class='red'>$4</span>$5<span class='green'>$6</span>$7<span class='blue'>$8</span><span class='green'>$9</span>$10<span class='blue'>$11</span>$12<span class='blue'>$13</span>$14

echo "<br>";

$string = "あらりうるえれおろかやゆよかまみきむめもく";

echo preg_replace($pattern, $replacement, $string);
echo "<br><br>";
echo htmlentities(preg_replace($pattern, $replacement, $string));
// あ<span class='blue'>らり</span>う<span class='red'>る</span>え<span class='green'>れ</span>お<span class='blue'>ろかやゆ</span><span class='green'>よ</span>か<span class='blue'>まみ</span>き<span class='blue'>むめも</span>く
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

全然スマートではないですが、無理やりやってみました。
ひどいロジックですが参考までに。

/*初期設定*/
$str="あらりうるえれおろかやゆよかまみきむめもく";
$pettern="あ+う○え*お+か+き+く";

/*○をredに、+をblueに、`*をgreenに*/
$list=[["○","red"],["+","blue"],["*","green"]];
foreach($list as $val){
  $count=0;
  $pettern=preg_replace_callback("/".$val[0]."/u",function($m) use(&$count,$val){
    $count++;
    return "(?'".$val[1].$count."'.+)";
  },$pettern);
}
$pattern="/".$pettern."/u";

/*replace用のパターン完成*/
print $pattern;
print "<hr>";

/*offsetをとりながらパターンマッチ*/
if(preg_match($pattern,$str,$match,PREG_OFFSET_CAPTURE)){
  foreach($match as $key=>$val){
    if(!preg_match("/^(blue|green|red)/",$key,$m)){
      /*いらないデータは消して*/
      unset($match[$key]);
    }else{
      /*いるデータは拡張*/
      $match[$key][2]=$m[1];
    }
  }
  /*キャプチャしたoffsetの逆順でソート*/
  usort($match,function($x,$y){return $x[1]<$y[1];});
  /*マッチ状況を確認*/
  print_r($match);
  print "<hr>";

  /*後ろから順に置換*/
  foreach($match as $val){
    $str=substr_replace($str,"<span class='".$val[2]."'>".$val[0]."</span>",$val[1],strlen($val[0]));
  }
  /*結果表示*/
  print htmlspecialchars($str);
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/26 22:06

    蛇足

    return "(?'".$val[1].$count."'.+)";

    return "(?'".$val[1].$count."'.+?)";
    と変更すると、文字が最短マッチになるのでキレる箇所がかわります

    キャンセル

  • 2017/09/27 19:18

    動作確認できました。

    コールバック関数とかオフセットとか後ろから置換とか最短マッチとか、
    とても有益な勉強材料だと思いますので
    じっくり味あわせていただこうと思っております。

    ありがとうございました。

    キャンセル

+1

解決済ですが、考えてみたので投稿します。

<?php

$str="あらりうるえれおろかやゆよかまみきむめもく";
$pattern="あ+う○え*お+か+き+く";

function replace($pattern, $str) {
   $pattern = preg_split("//u", $pattern, null, PREG_SPLIT_NO_EMPTY);
   $str     = preg_split("//u", $str,     null, PREG_SPLIT_NO_EMPTY);
   $pattern = array_reverse($pattern);
   $str     = array_reverse($str);

   $result = array();
   for ($i = 0, $pos = 0; $i < count($pattern); $i++, $pos++) {
      if ($pos == count($str)) {
         return null;
      }
      if ($pattern[$i] == $str[$pos]) {
         $result[] = $str[$pos];
      } else {
         $result[] = '</span>';
         switch ($pattern[$i]) {
            case '○';
               $class = 'red';
               break;
            case '*';
               $class = 'green';
               break;
            case '+';
               $class = 'blue';
               if ($i < count($pattern) && $pattern[$i+1] == '+') {
                  break;
               }
               while ((count($pattern)-$i) < (count($str)-$pos) && $pattern[$i+1] != $str[$pos+1]) {
                  $result[] = $str[$pos];
                  $pos++;
               }
               break;
            default:
               return null;
         }
         $result[] = $str[$pos];
         $result[] = "<span class='${class}'>";
      }
   }
   $result = array_reverse($result);
   return implode($result);
}

print replace($pattern, $str) . "\n";

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    18610questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • 正規表現

    729questions

    正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

  • トップ
  • PHPに関する質問
  • マルチバイト正規表現もどきを元にエレガントもしくはシンプルに文字列置換をしたい