CSVをHTML化したいのですが、上下に同じデータが入っている場合は結合して出力したいのです。
PHPをうまく利用してできませんか?(イメージ図を添付)
こういうのは見つけたのですが、
↓
「エクセルシートをHTMLテーブルに変換しちゃう君 」がすごく便利
http://web-marketing.zako.org/web-tools/henkankun-html-tables-from-excel.html
↓
CSVデータが随時変更になるので、変換も自動で行えるような運用にしたいと思いPHPでHTMLソースコードを書きたいと思います。
参考になるサイトなどご存知ありませんか?
あまりスマートではありませんが、わかりやすさを優先して書いてみました。
が、まさかstr_getcsv()が実装されていないとは思わず…(環境によるのかな?)
素直にfgetcsv()を使うべきだったなぁと思いました。
csv2table.php
<?php include_once("str_getcsv.php"); echo csv2table("sample.csv"); function csv2table($csvFilename){ $csvData = str_getcsv(file_get_contents($csvFilename)); $table = array(); //セルのHTMLタグ $nrow = count($csvData); //行数 $ncol = count($csvData[0]); //列数 for($col=0;$col<$ncol;$col++){ for($row=0;$row<$nrow;){ $cell = trim($csvData[$row][$col]); //セルの内容 $mergeCnt = 1; //結合するセルの数 //現在のセルから下に向かって、同じものがあるか調べる for($search=$row+1;$search<$nrow;$search++){ if(trim($csvData[$search][$col]) == $cell){ $mergeCnt++; } else{ break; } } //同じデータが2つ以上続いていたらマージ $rowspan = (2 <= $mergeCnt)?" rowspan='".$mergeCnt."'":""; //tdタグを生成し、セルごとに配列に保存する。 $table[$row][$col] = "<td".$rowspan.">".$cell."</td>"; //結合した分だけ下がる $row += $mergeCnt; } } ksort($table); //行が乱れている場合があるので整える。 //出力データ生成 $result = "<table>\n"; foreach($table as $row => $cols){ $result .= "<tr>"; $result .= implode("", $cols); $result .= "</tr>\n"; } $result .= "</table>"; return $result; } ?>
str_getcsv.php
<?php if (!function_exists('str_getcsv')) { function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') { if (is_string($input) && !empty($input)) { $output = array(); $tmp = preg_split("/".$eol."/",$input); if (is_array($tmp) && !empty($tmp)) { while (list($line_num, $line) = each($tmp)) { if (preg_match("/".$escape.$enclosure."/",$line)) { while ($strlen = strlen($line)) { $pos_delimiter = strpos($line,$delimiter); $pos_enclosure_start = strpos($line,$enclosure); if ( is_int($pos_delimiter) && is_int($pos_enclosure_start) && ($pos_enclosure_start < $pos_delimiter) ) { $enclosed_str = substr($line,1); $pos_enclosure_end = strpos($enclosed_str,$enclosure); $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end); $output[$line_num][] = $enclosed_str; $offset = $pos_enclosure_end+3; } else { if (empty($pos_delimiter) && empty($pos_enclosure_start)) { $output[$line_num][] = substr($line,0); $offset = strlen($line); } else { $output[$line_num][] = substr($line,0,$pos_delimiter); $offset = ( !empty($pos_enclosure_start) && ($pos_enclosure_start < $pos_delimiter) ) ?$pos_enclosure_start :$pos_delimiter+1; } } $line = substr($line,$offset); } } else { $line = preg_split("/".$delimiter."/",$line); /* * Validating against pesky extra line breaks creating false rows. */ if (is_array($line) && !empty($line[0])) { $output[$line_num] = $line; } } } return $output; } else { return false; } } else { return false; } } } ?>
sample.csv
a,b,c,d a,b,b,e a,c,b,f
出力結果
<table> <tr><td rowspan='3'>a</td><td rowspan='2'>b</td><td>c</td><td>d</td></tr> <tr><td rowspan='2'>b</td><td>e</td></tr> <tr><td>c</td><td>f</td></tr> </table>
データであるならば、Excelのマクロで組んじゃった方が早い様な気がしますが
そうではないんでしょうね、恐らく…。
因みに、HTMLのTable単独では確か「互い違いに行(列)を跨ぐ結合」は出来なかったはずです。
例えば「1列目は1~3行目が結合、2列目は2~5行目が結合」という様なことは
不可能なはずで、petemさんのデータでそういうケースが生じ得るならば
多重Table(Table内Table)等の特殊な対策が必要になるのではないでしょうか。
表の場合は必ず項目の列があると思いますが、ないのでしょうか?
ただ、上下のデータを比較してテーブルを作成していくのは難しそうですね・・・
1.csvのデータを二次元配列aに読み込んでしまう
2.同サイズの配列bを用意して、縦方向(下方向)にスキャンした情報を保存
3.配列aと配列bを組み合わせてタグを生成
どの程度スッキリとした処理ができるかは2のアルゴリズムに大きく依存するので、あとは他力本願
RTtool
http://www.rubyist.net/~rubikitch/computer/rttool/index.html
仰せの通りです…その条件(「連結しない列が一つでもあれば」)が
満たされていれば大丈夫でした…うっかりしておりました(汗)。
例えば左端or右端に見出し列があって、そこに関しては絶対にセル結合を行なわない
ということであれば問題無いと思うのですが、全列において結合の可能性があるなら
やっぱり1つのTableでは不可能となるケースが出て来るかも知れないですよね。
VBAだったらそんなに難しくないと思いますけど、どうでしょw。
質問者がVBA for Excelで構わないと仰せなら組んでみますけど。
説明がちょっと不足したにも関わらず
コメント欄にてお答えくださりありがとうございます。
「連結しない列」はあります。
イメージ図のC列のように、一番最後の列は独立しています。
一つのCSVデータから数十ページ精製します。
更新頻度は高くはないのですが、作業に手間取りそうだったので
VBAではなくPHPでのhtml精製を考えていました。
Rubyは経験がなかったので考えていませんでした。
Numericさんに書いていただいたコードを試してきたいと思います。
ありがとうございました。
>「連結しない列」はあります。
とのこと、安心しました。
ちなみにPHPでの自動生成というのは、
「アクセスするたびに生成処理実行」ということでしょうか?
アクセス数の多いサイトであれば、処理速度やサーバ負荷軽減のため
内部的にキャッシュをもたせるなど工夫の余地はあると思います。
また、エラー処理などはしていませんので、
必要に応じて適宜追加してください。
ありがとうございます。
>処理速度やサーバ負荷軽減のため
>内部的にキャッシュをもたせるなど工夫の余地はあると思います。
そこそこアクセスのあるページへの実装を考えています。
そのあたりの知識が乏しく、無駄に負荷をかけてしまっている可能性が高いので
勉強してみます。
このサイトを参考に見てみました。
http://moviesearch1.blog15.fc2.com/blog-entry-29.html
アクセス数は多かれど、
同じ人が数回アクセスするタイプのページではないので、
そこまでキャッシュを意識する必要はないかもしれません。
逆に、phpで随時読み込もうという目論見事態が破綻する可能性がありますが。。。
ありがとうございました。
>同じ人が数回アクセスするタイプのページではないので、
>そこまでキャッシュを意識する必要はないかもしれません。
あるページを参照したとき、どの閲覧者に対しても同じ結果が出力されるのであれば
・毎回CSVからテーブルを生成する
よりも
・初回はCSVから生成し、そのデータをキャッシュファイル(.txt等)に保存
・2回目以降は保存されたキャッシュファイルを読み込み、表示
のほうが効率的ではないかと思ったので、先ほどのコメントを書いた次第です。
ただ、CSVが更新されたかどうかをチェックする必要がでてきますので
そこもまた「工夫」が必要だと思います。
CSV更新時に管理ツール等から手動でキャッシュファイル(上記テキストファイル)を作成する、
アクセスの度に毎回CSVファイルの更新時刻を確認し、生成するかどうかの判断をする、など・・・
方法はたくさんありますので、いろいろ考えてみてください。
長々と失礼しました。