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

PHPで下記のように記述すると、HTMLのリスト(ul ol)として出力させたいのですが、うまくいかなくて困っています。

記述方法は、
1. 通常のリストは「-」、番号付きリストは「+」を行頭に付ける
2. 更に行頭に1つ以上の半角スペースを付けると、入れ子構造になる(入れ子の階層に制限なし)

となり、問題なく動作させるにはどうしたらよいでしょうか?

できればソース付きで回答して頂くと大変助かります。
よろしくお願いします。

■入力テキスト
-リスト1
-リスト2
+番号付きリスト2-A
+番号付きリスト2-B
-リスト3-A
-リスト3-B
+番号付きリスト2-C
-リスト3

■出力結果
<ul>
<li>リスト1</li>
<li>リスト2
<ol>
<li>番号付きリスト2-A</li>
<li>番号付きリスト2-B
<ul>
<li>リスト3-A</li>
<li>リスト3-B</li>
</ul>
</li>
<li>番号付きリスト2-C</li>
</ol>
</li>
<li>リスト3</li>
</ul>


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

▽最新の回答へ

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

どの行にも + か - があるものという前提です。

<?php
mb_internal_encoding('utf-8');

$text =<<< EOT
-リスト1
-リスト2
 +番号付きリスト2-A
 +番号付きリスト2-B
 -リスト3-A
 -リスト3-B
 +番号付きリスト2-C
-リスト3

EOT;

function text2list($text) {
 $data = explode("\n", $text);
 $n = count($data);
 $output = '';
 $level = -1;
 for ($i = 0; $i < $n; $i++) {
 $pos = strpos($data[$i], '+');
 if ($pos == FALSE) {
 $pos = strpos($data[$i], '-');
 }
 if ($pos < $level) {
 for ($j = $level; $j > $pos; $j--) {
 $output .= '</' . $tag[$level] . ">\n";
 }
 $output .= '<li>' . substr($data[$i], $pos + 1) . "</li>\n";
 $level = $pos;
 } else if ($pos > $level) {
 $ch = substr($data[$i], $pos, 1);
 if ($ch == '+') $c = 'ol';
 else $c = 'ul';
 for ($j = $level + 1; $j <= $pos; $j++) {
 $tag[$j] = $c;
 $output .= '<' . $tag[$j] . ">\n";
 }
 $output .= '<li>' . substr($data[$i], $pos + 1) . "</li>\n";
 $level = $pos;
 } else if ($data[$i] == '') {
 $output .= '</' . $tag[0] . ">\n";
 } else {
 $output .= '<li>' . substr($data[$i], $pos + 1) . "</li>\n";
 }
 }
 return $output;
}

$output = text2list($text);
echo $output;
?>

wankodonさんのコメント
遅れましたが、回答有り難うございました。 参考にして色々試したいと思います。

2 ● GoldenDawn
●150ポイント ベストアンサー
<?php
$input = <<< ENDOFINPUT
-リスト1
-リスト2
 +番号付きリスト2-A
 +番号付きリスト2-B
 -リスト3-A
 -リスト3-B
 +番号付きリスト2-C
-リスト3
ENDOFINPUT;

$lines = preg_split('/[\r\n]+/', $input) ;

// リストのタグを全て閉じる
function close_lists($d) {
 while ($l = array_pop($d)) {
 echo "</li>\n".
 str_repeat(' ', count($d)*4). // インデント調整
 ($l[1] == '-' ? '</ul>' : '</ol>') ;
 }
 echo "\n" ;
 return array() ;
}

$d = array() ; // 現在の階層を保存

while ($l = array_shift($lines)) {
 if (preg_match('/^( *)([-+])(.*)$/', $l, $m)) {
 $e = end($d) ; // 現在の階層

 // インデントが浅いか種類が変わればタグを閉じる
 while ($d && ($e[0] > $m[1] || ($e[0] == $m[1] && $e[1] != $m[2]))) {
 array_pop($d) ;
 echo "</li>\n".
 str_repeat(' ', count($d)*4). // インデント調整
 ($e[1] == '-' ? '</ul>' : '</ol>') ;
 $e = end($d) ;
 }

 // 階層を増やす
 if (!count($d) || $e[0] < $m[1]) {
 $d[] = array($m[1], $m[2]) ;
 if ($e) echo "\n" ;
 echo str_repeat(' ', (count($d)-1)*4). // インデント調整
 ($m[2] == '-' ? '<ul>' : '<ol>')."\n" ;
 }
 else echo "</li>\n" ;

 // リストの要素
 echo str_repeat(' ', count($d)*4-2). // インデント調整
 '<li>'.$m[3] ;
 }
 // リストの記号がない
 else {
 $d = close_lists($d) ;
 echo "$l\n" ;
 }
}

close_lists($d) ;
?>

出力例

<ul>
 <li>リスト1</li>
 <li>リスト2
 <ol>
 <li>番号付きリスト2-A</li>
 <li>番号付きリスト2-B
 <ul>
 <li>リスト3-A</li>
 <li>リスト3-B</li>
 </ul></li>
 <li>番号付きリスト2-C</li>
 </ol></li>
 <li>リスト3</li>
</ul>

wankodonさんのコメント
遅れましたが、シンプルな実装でとても勉強になります。 こんなやり方があったとは。回答どうも有り難うございました。
関連質問

●質問をもっと探す●



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