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

PHPで複雑なソート?させたい

http://pastebin.com/mse0s95i
アンカが付いていたらその下にレスを持っていきたいのですが
どうしたらそのようなソートができますか?
コードを書いていただけるとありがたいです。

●質問者: vas
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● だわかき
●50ポイント

たぶん、ソートではないと思います。

要求要件の確認です。

  1. アンカが複数付いていたらどうしますか?
  2. レス先で、さらにアンカが付いていたらどうしますか?

vasさんのコメント
複数付いていたらそのレスをコピー?して下に持ってくる さらにアンカが付いてる場合はその下並べていきたい

だわかきさんのコメント
アンカが参照元を指していると無限ループになってしまいます。 どうしましょうか?

2 ● kaji0120
●100ポイント

できました。こんな感じでどうでしょう。
大体どんな感じになったかが伝われば幸いです。

$lines = file($urlf);
$str=$lines;
$tmp_arr=array();
$res_arr=array();
foreach ($str as $line_num => $line) {
 
 $number = $line_num+1 ;
 //レス番
 
 $block = split("<>", $line);
 $name = $block[0];
 //名前
 $mail = $block[1];
 //メールアドレス
 $about = $block[2];
 //日付 IDSSS
 $text = $block[3];
 //本文
 $title = $block[4];
 //一行目のみ スレタイ

 

 preg_match_all('{<a href.+?&gt;&gt;(.+?)</a>}', $text, $ank) ;
 $line_html = "" .$number.": ".$name."<>".$mail."<>".$about."<>".$text."<>";

/*
アンカーが存在するのなら、たとえば20番のレスなら$tmp_arrに要素array(20,20の$line_html)を"前から"追加する。
今1から順に1000までforeeachしているが、このうち101と330と989にアンカーがついていたら、
$tmp_arr=array(array(989,989の$line_html),array(330,330の$line_html),array(101,101の$line_html));
なぜ前から追加するかは後述
 */

if(!empty($ank[1])){
 array_splice($tmp_arr, 0, 0, array($number,$line_html));
 }
array_push($res_array,$line_html);
}
 foreach($tmp_arr as $t){
$anc_num=$t[0];//アンカーのレス番
$anc_txt=$t[1];//アンカーの$line_html

/*
今、$res_arrには1レス目が0番目の要素、20レス目は19番目の要素に入っている。
アンカーの対象が1なら1番目、20番目なら20番目に導入する。

この時、たとえば20番目と40番目に挿入するとき、20番目に挿入するとそれ以外の番号が全て1つずつずれてしまうので、
40番目を代入するときは41番目に挿入しなければいけない。

しかし先に40番目を挿入すると、40番より前の番号はずれないため、若い番号は特に何もせずとも使用できる。
現在配列は、
$tmp_arr=array(array(989,989の$line_html),array(330,330の$line_html),array(101,101の$line_html));
のような形になっているため、foreachで20番より先に40番、100番より900番のレス番の処理を行える。
*/
array_splice($res_arr, $anc_num, 0, $anc_txt);

 }


kaji0120さんのコメント
失礼しました。この方式ですと >複数付いていたらそのレスをコピー?して下に持ってくる は達成できますが >さらにアンカが付いてる場合はその下並べていきたい が達成できませんね。

3 ● tobeoscontinue
●150ポイント ベストアンサー

datの内容を配列で保持することでアンカーが付いている場合に対応する行を参照して下にコピーしたような機能を実現してみました。

function file_convert_encoding($name, $to='utf-8', $from='cp932') {
 return explode("\n", mb_convert_encoding(file_get_contents($name), $to, $from));
}

文字変換機能を追加したfileです。文字変換が必要なければこれを使わずfileのままで問題ありません。

function array_dat($lines) {
 $kname = array('name', 'mail', 'about', 'text', 'title');
 $msgs = array();
 foreach ($lines as $line) {
 $items = split("<>", $line); // 最後に\nがあると空行が入る
 if (count($items) == 5) {
 $block = array_combine($kname, $items);
 if (preg_match_all('{<a href.+?&gt;&gt;(.+?)</a>}', $block['text'], $ank) > 0 )
 $block['ank'] = $ank[1];
 $msgs[] = $block;
 }
 }
 return $msgs;
}

datの内容をname, mail, about, text, titleとankの五列で構成された二次元の配列にして返します。
array_combineはキーと値の個数が合わないとワーニングが出るので別の方法が必要かもしれません。

function show_dat($number, $dats, $lvl=0) {
 extract($dats["$number"-1]);
 $line_html = str_repeat(' ', $lvl).$number.": ".$name."<>".$mail."<>".$about."<>".$text."<>\n";
 if (isset($ank))
 foreach ($ank as $s) {
 $ex = preg_split('/([-,])/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
 if (count($ex) == 1) {
 if ($s < $number) $line_html .= show_dat($s, $dats, $lvl+1);
 } else {
 list($f, $p, $t) = $ex;
 foreach(range($f, $t) as $s)
 if ($s < $number) $line_html .= show_dat($s, $dats, $lvl+1);
 }
 }
 return $line_html;
}

show_datが肝の部分で$datsの$number行目の部分を結合して返します。
その行に$ankがある場合はその行を再度show_datすることで
『アンカが付いてる場合はその下並べる』ことになります。
if ($s < $number) することで現在の行より上(小さい数)の行のみ処理することで無限ループしないよう対処しています。
$ankをforeachすることで複数のアンカに対応しています。
$ankが1-2のような場合があったのでpreg_splitを付加したので複雑になっていますしまだ対応が十分ではありません。
str_repeat(' ', $lvl)で文字下げを実現しています(htmlでは意味が無い)が必要ないなら削除可能です。或いは$lvl+1しない。
配列$datsを引数で渡しているので効率が悪いかもしれません。グローバルを使うか参照渡しにする。

$dats = array_dat(file_convert_encoding($urlf));
$line_html = '';
foreach (range(1, count($dats)) as $n)
 $line_html .= show_dat($n, $dats);
echo $line_html;

array_dat()でdatを配列に取り込みます。
後はforeachで各行を順番にshow_dat()で文字列にしてそれを結合していくだけです。
アンカがあればshow_datの内部で再帰的にshow_datされるので下に付加されていきます。

関連質問

●質問をもっと探す●



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