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

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

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

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

正規表現

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

Q&A

解決済

1回答

1503閲覧

マッチの中のサブマッチを別々に処理する方法

munyagu

総合スコア479

PHP

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

正規表現

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

0グッド

0クリップ

投稿2016/09/22 04:13

###前提・実現したいこと
一つのマッチの中に複数のサブマッチがある場合、別々のマッチとして受け取って処理したいです。
(もしかしたら、マッチ、サブマッチという用語が適切でないかもしれませんが)

例えば、

html

1<input type="text" name="your_name" class="short" /> 2<input type="text" name="your_email" class="middle" />

というHTMLタグがある場合にそれぞれのinputタグについて、typeの位置とclassの位置を取得して、アトリビュート名を変更したいのです。
typeをtitleに変更、classをidに変更、といったように。

typeとclassを別々にマッチさせれば処理できることは分かっているのですが、対象のHTMLファイルが大きいので、一度のマッチで処理したいと考えています。

お分かりになる方、ご教授いただけないでしょうか。

###発生している問題・エラーメッセージ

一つだけならマッチさせることができたが、複数をマッチさせるにはどうしたらいいか分からない。

###該当のソースコード
phpのpreg_match_allを使った単一の場合のマッチング。
思いどおりに動作します。

php

1$input = <<<EOT 2<input type="text" name="your_name" class="short" /> 3<input type="text" name="your_email" class="middle" /> 4EOT; 5 6// インプットタグのtypeアトリビュートの位置を取得 7$pattern = '/<input\s.*?(type).*?>/i'; 8$result = preg_match_all($pattern, $input, $matches, PREG_OFFSET_CAPTURE); 9 10var_dump($matches);

出力(単一の場合は想定どおり)

array(2) { [0]=> array(2) { [0]=> array(2) { [0]=> string(52) "<input type="text" name="your_name" class="short" />" [1]=> int(0) } [1]=> array(2) { [0]=> string(54) "<input type="text" name="your_email" class="middle" />" [1]=> int(54) } } [1]=> array(2) { [0]=> array(2) { [0]=> string(4) "type" [1]=> int(7) } [1]=> array(2) { [0]=> string(4) "type" [1]=> int(61) } } }

下記のように、同一マッチに複数のサブマッチをさせたい場合にどうしたらいいか分かりません。
###試したこと
classアトリビュートの位置も取得したい場合、コード中のパターンを以下のようにしてみました。
間違っている気がしつつ、実行して見ましたがうまくいきません。

php

1// インプットタグのtypeとclassアトリビュートの位置を取得 2$pattern = '/<input\s.*?(type)(class).*?>/i';

出力

array(3) { [0]=> array(0) { } [1]=> array(0) { } [2]=> array(0) { } }

期待する出力は以下のようなイメージのものです。
もしかしたら、ネストもうひとつが深くなるのかもしれませんが。

array(2) { [0]=> array(4) { [0]=> array(2) { [0]=> string(52) "<input type="text" name="your_name" class="short" />" [1]=> int(0) } [1]=> array(2) { [0]=> string(52) "<input type="text" name="your_name" class="short" />" [1]=> int(0) } [2]=> array(2) { [0]=> string(54) "<input type="text" name="your_email" class="middle" />" [1]=> int(54) } [3]=> array(2) { [0]=> string(54) "<input type="text" name="your_email" class="middle" />" [1]=> int(54) } } [1]=> array(4) { [0]=> array(2) { [0]=> string(4) "type" [1]=> int(7) } [1]=> array(2) { [0]=> string(4) "class" [1]=> int(36) } [2]=> array(2) { [0]=> string(4) "type" [1]=> int(61) } [3]=> array(2) { [0]=> string(4) "class" [1]=> int(91) } } }

他にも、

php

1$pattern = '/<input\s.*?(type).*?>|<input\s.*?(class).*?>/i';

も試しましたが、classはマッチしませんでした。

###補足情報(言語/FW/ツール等のバージョンなど)
より詳細な情報

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

popobot

2016/09/22 05:23 編集

preg_matchでマッチさせたいというよりpreg_replaceで置き換えたいということですか? 最終的には以下のような形にしたいということですか? そこだけ置き換えたHTML全体がほしいのですか? <input title="text" name="your_name" id ="short" /> <input title="text" name="your_email" id="middle" />
munyagu

2016/09/22 05:25

そうなんです。 最終的にはおっしゃるとおりに置き換えたHTML全体が欲しいのです。 対象のアトリビュートは例なので必ずしもtypeを置き換えたいわけではありませんが、趣旨はおっしゃられているとおりです。 しかし、私の正規表現では、nameアトリビュートの中にtypeなどの文字列が合った場合も対象となってしまい、全然だめですね・・・
guest

回答1

0

ベストアンサー

正規表現でやるならこんな感じでしょうか

<?php $input = <<<EOT <input type="text" name="your_name" class="short" /> <input type="text" name="your_email" class="middle" /> EOT; $patterns = array(); $patterns[0] = '|(<input.*)(type)(\s*=)(.*/>)|'; $patterns[1] = '|(<input.*)(class)(\s*=)(.*/>)|'; $replacements = array(); $replacements[0] = '${1}title${3}${4}'; $replacements[1] = '${1}id${3}${4}'; $result = preg_replace($patterns, $replacements, $input); var_dump($result);

※preg_replaceはパターンを複数指定できます。
※制限として、文字列マッチングなので、以下のようなinputがあると負けます。

<input type="text" name="type=aaaa" class="middle" />

多くの場合、大丈夫だと思いますが、より厳密にやりたければHTML解析(DomDocument等)して置き換える方法もあります。


追記

上記の2パターンのpreg_replaceだと、内部的には2回走査する気がしています。
なので、preg_replace_callbackを使って1パターンでできないか試してみましたのが以下のコードです。
一応うまく行っていますが、input内にtypeとclassのどちらかしかない場合に対応していません。
頑張ればできますが、これ以上複雑になるとバグがないか確認するのが難しくなってくるのでやめました...
個人的には、多少処理が重くても、わかりやすい実装を好むので、2パターンのpreg_replaceを使うのがおすすめです

php

1<?php 2$input = <<<EOT 3<input type="text" name="your_name" class="short" /> 4<input type="text" name="your_email" class="middle" /> 5EOT; 6 7$result = preg_replace_callback( 8 '/(<input.*)(type|class)(\s*=)(.*)(type|class)(\s*=)(.*)(\/>)/', 9 function ($matches) { 10 $replaces = array('type' => 'title', 'class' => 'id'); 11 return $matches[1] 12 .$replaces[$matches[2]].$matches[3].$matches[4] 13 .$replaces[$matches[5]].$matches[6].$matches[7] 14 .$matches[8]; 15 }, 16 $input 17); 18 19var_dump($result);

投稿2016/09/22 07:20

編集2016/09/22 09:19
popobot

総合スコア6586

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

popobot

2016/09/22 07:53 編集

でもこの複数パターン指定は、内部的には2回やっているのと同じかも...根拠はないですが。 preg_replace_callbackを使えばパターン1個でできそうな気がしてきた... あとで時間があったらやってみます or やってみてください。
munyagu

2016/09/22 13:23

色々教えていただき、とても参考になりました。 そうですね、やはり複数回置き換えを行うことにします。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問