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

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

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

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

正規表現

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

Q&A

1回答

1110閲覧

【PHP】正規表現を使用して再帰処理を行い、HTMLをタグとフレーズに分割してまた元に戻す方法

maresuke

総合スコア16

PHP

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

正規表現

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

0グッド

0クリップ

投稿2020/08/04 02:55

お世話になります。
考えて考えて考えたのですが原因がわからないので、どなたかわかる方、ご教示いただきたいと思います。

PHPとlinuxを用いて、以下のルールでHTMLをフレーズとタグに分離したいと思います。
HTMLのフレーズ部を独自の記号 (例:TAG_1234)に置換して、タグも分離して配列に入力します。
そのあと配列データを再度置換する形で、元のデータに戻したいです。

例として:

<body> <p>I have many <a href="./beautiful.html">beautiful chopsticks</a>.</p> <table> <tr> <td>cat1</td> <td><a href="./2">cat2</a></td> </tr> </table> </body>

<body> <p>I have many <a href="./beautiful.html"> TAG_1 </a></p> <table> <tr> <td> TAG_2 </td> <td><a href="./2"> TAG_3 </a></td> </tr> </table> </body>

<body> <p>I have many TAG_4 </p> <table> <tr> TAG_5 <td>TAG_6</td> </tr> </table> </body>

<br>
<br>
次の置換は I have many TAG_4TAG_6に、最後には、<body> TAG_(XXX) </body>TAG_(XXX+1)に置換します。
置換したフレーズを格納した配列は、

$result_phrase=array(//フレーズ部 'TAG_1'=>"beautiful chopsticks", 'TAG_2'=>"cat1", : 'TAG_6'=>"I have many TAG_4", : ); $result_tag=array(//タグ部 'TAG_4'=>"<a href="./bueautiful.html"> TAG_1 </a>", 'TAG_5'=>"<td> TAG_2 </td>", : );

のような形となるはずです。
このようなパースと再構築を実現するために、PHPで正規表現を使用して以下のようなコードを作成しました。

<?php function str_replace_first($from,$to,$content){ $from = '/'.preg_quote($from, '/').'/'; return preg_replace($from, $to, $content, 1); } function html_parse($html){ $result_tag = array();//tag container $result_phrase = array();//phrase container $pattern_tag = '#(<[^>]+?> T_[0-9]+? </[^>]+?>)#';//tag finder $pattern_phrase = '#<[^>]+?>([^<]+?)</[^>]+?>#';//phrase finder $control = 1;//再帰処理回数 $num = 0;//置換回数 while ($control<10) { $precon = count($result_tag) + count($result_phrase); $start = 0; while (preg_match_all($pattern_tag, $html, $match_tag)) { foreach ($match_tag[1] as $m) { $result_tag[$num] = $m; $html = str_replace_first($m, " T_{$num} ", $html); $num++; } //foreach end } //if pregmatch end if (preg_match_all($pattern_phrase, $html, $match_phrase)) { foreach ($match_phrase[1] as $m) { $result_phrase[$num] = $m; $html = str_replace_first($m, " T_{$num} ", $html); //echo $m; $num++; } //foreach end } //if end $control=count($result_tag) + count($result_parse)- $precon; } return array('tag'=>$result_tag,'phrase'=>$result_phrase); } //HTMLのパース $parse=$html_parse($html); $result_tag=$parse['tag']; $result_phrase=$parse['phrase']; //HTMLの再構築 $in_arr = array_merge($result_tag,$result_phrase); for ($num = count($in_arr) - 1; $num >= 0; $num--) { $html = str_replace_first("T_{$num}", $in_arr[$num], $html); } echo $html;//最初のHTMLと同じものがechoされるはず

想定では、このコードでechoされるのは最初のHTMLと同じものとなるはずです。
しかし、複数のwebページで実行したところ、同じものとなりませんでした。
コードのどこがおかしいのか、ご教示ください。

また、こんな複雑な方法を使わなくてももっと簡単に

$result_phrase=array(//フレーズ部 'TAG_1'=>"beautiful chopsticks", 'TAG_2'=>"cat1", : 'TAG_6'=>"I have many TAG_4", : ); $result_tag=array(//タグ部 'TAG_4'=>"<a href="./bueautiful.html"> TAG_1 </a>", 'TAG_5'=>"<td> TAG_2 </td>", : );

の様に分離する方法がありましたら、お教えいただけると幸いです。

読んでいただきありがとうございました。
よろしくお願いいたします。

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

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

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

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

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

maisumakun

2020/08/04 02:58

正規表現でなければならない事情はありますか?(PHPでもDOMが利用可能です)
退会済みユーザー

退会済みユーザー

2020/08/04 03:28

おいらも、そういうことをやるなら、DOM操作してXPath式駆使してがんばっちゃう。
guest

回答1

0

まずこれは正規表現でやることではありません。
Domdocumentを利用してください。
それとTAG1-3まではわかりますが、それがどういう意図で4-6になるか
わかりません(TAG5は明らかに文法違反です)

投稿2020/08/04 03:32

yambejp

総合スコア116724

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

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

yambejp

2020/08/04 03:32

こんな感じ <?PHP $html=<<<eof <body> <p>I have many <a href="./beautiful.html">beautiful chopsticks</a>.</p> <table> <tr> <td>cat1</td> <td><a href="./2">cat2</a></td> </tr> </table> </body> eof; $dom=new Domdocument(); $dom->loadHTML($html); $nodes=$dom->getElementsByTagName("*"); $count=1; $data=[]; foreach($nodes as $node){ if($node->getElementsByTagName("*")->length==0 and $node->nodeValue!==""){ $tag="TAG_".($count++); $data[$tag]=$node->nodeValue; $node->nodeValue=" ".$tag." "; } } $body = $dom->getElementsByTagName("body")[0]; print htmlspecialchars($dom->saveHTML($body)); print "<hr>"; print_r($data);
maresuke

2020/08/04 16:22

ご回答ありがとうございます。 htmlをテキスト文書に見立てたうえで、以下のように可逆的に、すなわち元のHTMLに復元できるようにフレーズ部を取り出す方法を考えたいのです。 というのも(あまり出来が良くないですが)自作の機械翻訳でウェブサイトを訳せるようにしてみようと思いまして、HTMLからタグ構造を維持したままフレーズ部を取り出すことを検討しているのです。 DOMの存在を知って使用することも考えたのですが、正規表現と違い今までDOMを使用したことが一度もなく、これを機会にPHP.NETのマニュアルを読み、調べましたがDOMからHTML全域のフレーズ部のみを取り出す方法を見つけることができませんでした。 指定タグで候補を抽出する機能があるようですが、フレーズ部があるタグの中で<p>や<h1>以外のタグが使用されている場合、翻訳漏れが出てくること、また私の翻訳システムでは、<p>タグを選択してフレーズを取り出しても、取り出したフレーズの中に<a>タグが含まれている場合、翻訳コマンドを実行しても<a>~</a>部を正常に翻訳できない欠点があります。 手作業で<a>タグ内を翻訳しても翻訳前後で変化しない仮の単語TAG_1234などで置換して<a>タグ内を別個に訳せば読める文章ができました。そのためフレーズ部にa,spanなどのタグを含むのであれば、再帰的に置換する必要があると考えました。 そのような理由があり、以下のような置換処理を考えました。 ・任意の自然数に対して文字列「TAG_X」を特殊表記と呼称する。 ・タグ内にタグがある場合は、以下の処理を再帰的に適応する。 ・タグ内にタグがなく、「タグ内にフレーズがある場合」は、「フレーズを」特殊表記に置換する。 ・タグ内にタグがなく、「タグ内にフレーズがない、もしくはタグ内のフレーズに特殊表記しか含まない場合」は、「タグ全体を」特殊表記へ置換する。(例えば<p>TAG_1427</p>や<div></div>、<img src=""~~~>など) 例えばW,X,Y,Zを整数とすると、<p>This is <a href="./a.html">a</a> pen.</p>では、まず<a href="./a.html">a</a>部のaをTAG_Wで置換して、<p>This is <a href="./a.html">TAG_W</a> pen.</p>とします。次に<p>This is <a href="./a.html">TAG_W</a> pen.</p>をTAG_Xで置換して、<p>This is TAG_X pen.</p>とします。 次にThis is TAG_X pen.をTAG_Yで置換して<p>TAG_Y</p>となり、最後にこれをTAG_Zで置換します。 元のHTMLファイルにはTAG_Zが残っているため、HTMLを復元する際にはTAG_Zを<p>TAG_Y</p>に置換します。 次にTAG_YをThis is TAG_X pen.で置換して、<p>This is TAG_X pen.</p>、最後に同様にTAG_Xを<a href="./a.html">a</a>で置換して、<p>This is <a href="./a.html">a</a> pen.</p>です。 上の例ではTAG_W、TAG_Yを翻訳にかければ、正常な訳文が表示されます。 つたない技量で作成している翻訳システムですが、学べることが多く楽しいのです。 もしDOMを使用して効率的にHTMLを解析する方法などがあれば、ご教示いただけると幸いです。 この度は御解答いただき、誠にありがとうございました。 あらためて心より御礼申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問