1285677495 CSVをHTML化する際での質問です。




CSVをHTML化したいのですが、上下に同じデータが入っている場合は結合して出力したいのです。
PHPをうまく利用してできませんか?(イメージ図を添付)


こういうのは見つけたのですが、

「エクセルシートをHTMLテーブルに変換しちゃう君 」がすごく便利
http://web-marketing.zako.org/web-tools/henkankun-html-tables-from-excel.html

CSVデータが随時変更になるので、変換も自動で行えるような運用にしたいと思いPHPでHTMLソースコードを書きたいと思います。


参考になるサイトなどご存知ありませんか?

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2010/10/05 21:40:02
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:Numeric No.1

回答回数83ベストアンサー獲得回数18

ポイント60pt

あまりスマートではありませんが、わかりやすさを優先して書いてみました。

が、まさか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: str_getcsv - Manualより転載

<?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>
id:petem

うおぉ


すごい。。。

まさかここまでキッチリと答えてもらえるとは。。。

ありがとうございます。

試してみます。

2010/09/30 12:22:54
  • id:Silvanus
    随時変更されるCSVデータというのが、もし元々Excelのワークシート上に存在する
    データであるならば、Excelのマクロで組んじゃった方が早い様な気がしますが
    そうではないんでしょうね、恐らく…。
    因みに、HTMLのTable単独では確か「互い違いに行(列)を跨ぐ結合」は出来なかったはずです。
    例えば「1列目は1~3行目が結合、2列目は2~5行目が結合」という様なことは
    不可能なはずで、petemさんのデータでそういうケースが生じ得るならば
    多重Table(Table内Table)等の特殊な対策が必要になるのではないでしょうか。
  • id:rouge_2008
    質問のサンプルのように、連結しない列が一つでもあれば一つのテーブルで可能です。
    表の場合は必ず項目の列があると思いますが、ないのでしょうか?
    ただ、上下のデータを比較してテーブルを作成していくのは難しそうですね・・・
  • id:windofjuly
    うぃんど 2010/09/29 04:36:58
    最も簡単そうな手順
    1.csvのデータを二次元配列aに読み込んでしまう
    2.同サイズの配列bを用意して、縦方向(下方向)にスキャンした情報を保存
    3.配列aと配列bを組み合わせてタグを生成
    どの程度スッキリとした処理ができるかは2のアルゴリズムに大きく依存するので、あとは他力本願
  • id:longicorn
    CSVじゃないし、Rubyだけど参考までに。

    RTtool
    http://www.rubyist.net/~rubikitch/computer/rttool/index.html
  • id:Silvanus
    > rouge_2008さん
    仰せの通りです…その条件(「連結しない列が一つでもあれば」)が
    満たされていれば大丈夫でした…うっかりしておりました(汗)。
    例えば左端or右端に見出し列があって、そこに関しては絶対にセル結合を行なわない
    ということであれば問題無いと思うのですが、全列において結合の可能性があるなら
    やっぱり1つのTableでは不可能となるケースが出て来るかも知れないですよね。
    VBAだったらそんなに難しくないと思いますけど、どうでしょw。
    質問者がVBA for Excelで構わないと仰せなら組んでみますけど。
  • id:petem

    説明がちょっと不足したにも関わらず
    コメント欄にてお答えくださりありがとうございます。



    「連結しない列」はあります。
    イメージ図のC列のように、一番最後の列は独立しています。


    一つのCSVデータから数十ページ精製します。
    更新頻度は高くはないのですが、作業に手間取りそうだったので
    VBAではなくPHPでのhtml精製を考えていました。


    Rubyは経験がなかったので考えていませんでした。


    Numericさんに書いていただいたコードを試してきたいと思います。
    ありがとうございました。
  • id:Numeric
    コメント欄を呼んで気にはしていましたが、
    >「連結しない列」はあります。
    とのこと、安心しました。

    ちなみにPHPでの自動生成というのは、
    「アクセスするたびに生成処理実行」ということでしょうか?
    アクセス数の多いサイトであれば、処理速度やサーバ負荷軽減のため
    内部的にキャッシュをもたせるなど工夫の余地はあると思います。

    また、エラー処理などはしていませんので、
    必要に応じて適宜追加してください。
  • id:petem
    Numericさん

    ありがとうございます。

    >処理速度やサーバ負荷軽減のため
    >内部的にキャッシュをもたせるなど工夫の余地はあると思います。


    そこそこアクセスのあるページへの実装を考えています。
    そのあたりの知識が乏しく、無駄に負荷をかけてしまっている可能性が高いので
    勉強してみます。


    このサイトを参考に見てみました。
    http://moviesearch1.blog15.fc2.com/blog-entry-29.html

    アクセス数は多かれど、
    同じ人が数回アクセスするタイプのページではないので、
    そこまでキャッシュを意識する必要はないかもしれません。
    逆に、phpで随時読み込もうという目論見事態が破綻する可能性がありますが。。。


    ありがとうございました。
  • id:Numeric
    >アクセス数は多かれど、
    >同じ人が数回アクセスするタイプのページではないので、
    >そこまでキャッシュを意識する必要はないかもしれません。

    あるページを参照したとき、どの閲覧者に対しても同じ結果が出力されるのであれば

    ・毎回CSVからテーブルを生成する

    よりも

    ・初回はCSVから生成し、そのデータをキャッシュファイル(.txt等)に保存
    ・2回目以降は保存されたキャッシュファイルを読み込み、表示

    のほうが効率的ではないかと思ったので、先ほどのコメントを書いた次第です。
    ただ、CSVが更新されたかどうかをチェックする必要がでてきますので
    そこもまた「工夫」が必要だと思います。
    CSV更新時に管理ツール等から手動でキャッシュファイル(上記テキストファイル)を作成する、
    アクセスの度に毎回CSVファイルの更新時刻を確認し、生成するかどうかの判断をする、など・・・
    方法はたくさんありますので、いろいろ考えてみてください。

    長々と失礼しました。

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

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

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

回答リクエストを送信したユーザーはいません