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


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

回答の条件
  • 1人50回まで
  • 登録:2013/07/22 17:23:44
  • 終了:2013/07/25 19:42:46

ベストアンサー

id:tobeoscontinue No.3

tobeoscontinue回答回数213ベストアンサー獲得回数532013/07/24 00:16:51

ポイント150pt

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されるので下に付加されていきます。

その他の回答(2件)

id:dawakaki No.1

だわかき回答回数797ベストアンサー獲得回数1222013/07/22 19:47:31

ポイント50pt

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

要求要件の確認です。

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

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

2013/07/22 19:50:40
id:dawakaki

アンカが参照元を指していると無限ループになってしまいます。
どうしましょうか?

2013/07/23 08:47:54
id:kaji0120 No.2

kaji0120回答回数59ベストアンサー獲得回数132013/07/22 20:30:55

ポイント100pt

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

$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);
		
    }

id:kaji0120

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

2013/07/22 21:05:43
id:tobeoscontinue No.3

tobeoscontinue回答回数213ベストアンサー獲得回数532013/07/24 00:16:51ここでベストアンサー

ポイント150pt

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されるので下に付加されていきます。

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません