人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPの質問です
こちらの質問の続きです
http://q.hatena.ne.jp/1378940673
ソース1の場合はうまく動くのですがソース2の場合はうまく動きません
どこがダメなのでしょうか?

●質問者: takanii
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

質問者から

ソース1

<html><body>
<?php
 $s = '';
 $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>');
 if ( preg_match_all("#<tr[^<]+?(.+?)</tr#is", $b, $trs , PREG_SET_ORDER) ) {
 $a = array();
 foreach( $trs as $tr ) {
 if ( preg_match_all("#<td[^<]+?(.+?)</td#is", $tr[1], $tds , PREG_SET_ORDER) ) {
 $a[] = '<option value="' . $tds[0][1] . '">' . $tds[0][1] . '回</option>';
 }
 }
 $a = array_unique( $a, SORT_REGULAR );
 $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) );
 $s = '<select name="kai1">' . "\n";
 $s .= join( $a, "\n" ) . "\n";
 $s .= '</select>' . "\n";
 }
 echo ( $s );
?>
<TABLE>
<tr>
<td>1</td>
</tr>
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>3</td>
</tr>
</TABLE></body></html>

<html><body>
<?php
 $s = '';
 $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>');
 if ( preg_match_all("#<tr[^<]+?(.+?)</tr#is", $b, $trs , PREG_SET_ORDER) ) {
 $a = array();
 foreach( $trs as $tr ) {
 if ( preg_match_all("#<td[^<]+?(.+?)</td#is", $tr[1], $tds , PREG_SET_ORDER) ) {
 $a[] = '<option value="' . $tds[0][1] . '">' . $tds[0][1] . '回</option>';
 }
 }
 $a = array_unique( $a, SORT_REGULAR );
 $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) );
 $s = '<select name="kai1">' . "\n";
 $s .= join( $a, "\n" ) . "\n";
 $s .= '</select>' . "\n";
 }
 echo ( $s );
?>
<TABLE>
<tr>
<td><input type="checkbox" class="hikakubox"/></td>
<td>1</td>
</tr>
<tr>
<td><input type="checkbox" class="hikakubox"/></td>
<td>1</td>
</tr>
<tr>
<td><input type="checkbox" class="hikakubox"/></td>
<td>2</td>
</tr>
<tr>
<td><input type="checkbox" class="hikakubox"/></td>
<td>3</td>
</tr>
</TABLE></body></html>

1 ● うぃんど
●67ポイント

2ヶ所にある preg_match_all の正規表現改変(単純化しました)および、
2列目からの抜きだしへの改変($tds[0][1] →$tds[1][1])を行いました

<?php
 $s = '';
 $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>');
 if ( preg_match_all("#<tr.*?>(.*?)</tr.*?>#is", $b, $trs , PREG_SET_ORDER) ) {
 $a = array();
 foreach( $trs as $tr ) {
 if ( preg_match_all("#<td.*?>(.*?)</td.*?>#is", $tr[1], $tds , PREG_SET_ORDER) ) {
 $a[] = '<option value="' . $tds[1][1] . '">' . $tds[1][1] . '回</option>';
 }
 }
 $a = array_unique( $a, SORT_REGULAR );
 $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) );
 $s = '<select name="kai1">' . "\n";
 $s .= join( $a, "\n" ) . "\n";
 $s .= '</select>' . "\n";
 }
 echo ( $s );
?>

うぃんどさんのコメント
勘違いで申し訳ありません。 確認次第追記させていただきます。

うぃんどさんのコメント
回答文修正いたしました。 変更前 $tds[0][1] →$tds[1][1] の変更のみ 変更後 正規表現の見直しと$tds[0][1] →$tds[1][1]の変更

a-kuma3さんのコメント
多分、クイズっぽいテーブルのやつでしょうから、 >|| <td.*?>(.*?)</td.*? ||< は、 >|| <td.*?>(\d+)</td.*? ||< としておいた方が良いと思います。 SimpleXML って、XML 宣言が無くても使えるんでしたっけ? これ以上複雑になるなら、正規表現で切り出すより DOM でたどった方が良いのかなあ、と思ったり。

うぃんどさんのコメント
>¥d+ なんであのような形にしたんだろうと自分でも思いつつ、 正規表現をオーソドックスで単純な形に戻したわけなのですが…。 単純に数字のみと割り切ってよいかまでは判断つかず、そのままにしています…。 >SimpleXML って、XML 宣言 >DOM XML宣言は付け足せば済む話ですが、 HTMLソースの未整形・未整理のほうが気になります。 全体を見せるわけにはいかないのか、 ご自身で色々試行錯誤なさりたいのかがまだよくわからないのですが、 そろそろきっちりと全体構想と現状をお聞きして抜本的解決を図ったほうが良いかもしれませんね…。

takaniiさんのコメント
全体像をお教えして作っていただくのは申し訳ないと思い 勉強のために自分で少しづつ作っていって つまずいた時はこちらで質問させていただいております

うぃんどさんのコメント
回答を強制しているわけではないので、気にしなくて良いと思います。 小出しにすると、場当たり的な回答しかできなくなってしまいますので、 結果遠回りになり、手間が増えているだけの気がしているのです…。 ご質問の開始点から、すでに10日以上が過ぎ、質問数も20になろうかと思いますが、 このままでは倍の期間、倍の質問数になっても終わらないような気がしてなりません。 もちろん、それがご希望なら無理にとは申せませんし、 こちらとしましても、きっちりした回答にまとめるのに数日かかるかもしれませんので、 これまた無理にとは申せませんが、もう少しまとまった形でご質問していただくほうが、 たぶん、すっきりするものと思います。 週明けくらいまでは暇にしてる予定なので、この返信欄でお答えいただいても結構ですし、 お時間かかるようでしたら、受付中の質問の解決を優先していただいても結構です。 いずれも、あわてて質問を終了することなく、ゆっくりとお考えください。

2 ● a-kuma3
●67ポイント

正規表現の部分をいじりました。

<?php
 ...
 $a = array();
 foreach( $trs as $tr ) {
 if ( preg_match_all("#<td[^>]*>(\d+)</td>#is", $tr[1], $tds , PREG_SET_ORDER) ) {

ideone.com で確認したのがこちら。
http://ideone.com/UPi8ri


3 ● tobeoscontinue
●66ポイント

スクレイピングするなら正規表現を使うよりもsimple_html_domのようなDOMを使った方が簡単なように思います。
今回はインストール無しで使えるDOMDocumentを使って実装してみます。

function select_html($name, $opt, $select=NULL) {
 $o = array('<select name="'.$name.'">');
 foreach($opt as $k=>$v) {
 $s = ($v == $select) ? ' selected' : '';
 $o[] = ' <option value="'.$k.'"'.$s.'>'.$v.'</option>';
 }
 $o[] = '</select>';
 return join($o, "\n");
}

selectのhtml文を生成します。$optは配列で連想配列を想定していますが単に添字でもOKです。
値が$selectと同じだとselectedを付加します。キーと値でoptionを生成しているので主旨とずれていますが

' <option value="'.$v.'"'.$s.'>'.$v.'</option>';

とすることで希望するコードを生成します。

function file_get_dom($file) {
 $doc = new DOMDocument();
 $doc->loadHTMLFile($file);
 return $doc;
}

$file からDOMを生成して返します。htmlファイルの中に漢字などがあると文字化けを起すようなのでエンコードを指定する必要があるようなのですがnew DOMDocument('1.0','UTF-8')や$doc->encoding = 'UTF-8'
のようにしても解決しませんでした。htmlファイルの中にmetaで

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

とすることで解決できました。

function dom_get_table($doc, $n=0) {
 $table = array();
 $tbl = $doc->getElementsByTagName('table');
 foreach ($tbl->item($n)->getElementsByTagName('tr') as $r=>$tr) {
// foreach ($tr->childNodes as $c=>$td)
// if ($td->nodeName == 'td') $table["$c"]["$r"] = $td->nodeValue;
 foreach ($tr->childNodes as $td)
 if ($td->nodeName == 'td') $table["$r"][] = $td->nodeValue;
 }
 return $table;
}

$docのtableから二次元の配列にして返します。htmlの中にはtableが複数ある場合があると思いますので$nで何番目かを指定します。
本来はidなどで指定できたほうが無駄が無いと思いますが。
配列は行要素の中に列の配列が入っています。そのため後でtransposeで行と列を入れ替えています。

 foreach ($tr->childNodes as $c=>$td)
 if ($td->nodeName == 'td') $table["$c"]["$r"] = $td->nodeValue;

なら列要素の中に行の配列を入れることができるので後のtransposeをする必要がなくなります。

function transpose($org) {
 $trans = array();
 foreach ($org as $n=>$r)
 foreach ($r as $m=>$c) $trans["$m"]["$n"] = $c;
 return $trans;
}

二次元配列の行と列を入れ替えます。

$opt = transpose(dom_get_table(file_get_dom("hoge.html")));
$opt = array_unique($opt[1]);
echo select_html('問題番号', array_values($opt), end($opt));
//$opt = array_unique(array_column(dom_get_table(file_get_dom("hoge.html")),1));
//echo select_html('問題番号', array_values($opt), end($opt));

hoge.htmlをfile_get_domでDOMに格納します。
dom_get_tableでtableから二次元配列を生成します。
transposeで行と列を入れ替えます。
ソース1の場合は0列目なのでarray_uniqueするのは$opt[0]となります。
ソース2の場合は1列目なのでarray_uniqueするのは$opt[1]となります。
array_uniqueで重複があった場合、添字が飛ぶのでarray_valuesで付け替えています。
がselect_htmlで$kを使わない、或いは添字が飛んでもいいのであればする必要はありません。
PHP5.5からarray_columnが使えるようになり、二次元の配列から列を取り出すことができるのでtransposeする必要がありません。
それ以前でもhttp://www.php.net/manual/ja/function.array-column.phpからソースをコピペしても使えます。

$opt = array_unique(array_column(dom_get_table(file_get_dom("hoge.html")),1));
echo select_html('問題番号', array_values($opt), end($opt));

ソース1の場合は0列目なのでarray_column(dom_get_table(file_get_dom("hoge.html")),0)
ソース2の場合は1列目なのでarray_column(dom_get_table(file_get_dom("hoge.html")),1)

endは$optの配列ポインタを最後の要素に設定してしまうので以降に$optを参照する場合はresetする必要があります。

関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ