こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入

行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入しようとしています。
2000件ごとにファイルを分割し、再度各ファイルを読み込みなおして、insertしようと考えました。

ところが、ファイル分割で2000件ごとに区切った場合、最後の2000件に満たない端数分をファイルに落とす方法がわかりません。
どなたか教えていただけないでしょうか。

※かなり冗長な書き方をしているかと思いますので、改善点等あればご指摘いただければ幸いです。

以下サンプルコード------------------------------------------------------

$count = "0";
$max = "2000"; //ファイルあたりの行数
$file_count = "0"; //ファイル名につける連番

$RF = fopen( "base_file.csv", "r") or die("ファイルが開けません");
while (($data = fgetcsv($RF)) !== false) {
if($count < $max){
//順番を入れ替えたり、データをいじるかもしれないのでsprintf
$lines.= sprintf("%s,%s,%s,%s,%s,%s,%s\n",
$data[0],
$data[1],
$data[2],
str_replace(" "," ",$data[3]), //全角スペースを半角に変換
$data[4],
$data[5],
$data[6]
);
}
if($count == $max){
//ファイル名生成
$fileno = zerofill($file_count);
$filename = "./files/datafile_".$fileno.".dat";
$WF = fopen($filename, "w");
fputs($WF, $lines);
fclose($WF);

//書き込みデータを空にする
$lines = "";
$file_count++;
$count = "0";
}
$count++;
}

fclose($RF);


//ファイル名の連番を0で埋める
function zerofill($val){
if($val < 100){
if($val < 10){
$ret = "0".$val;
}
$ret = "00".$val;
}

return $ret;
}

投稿日時 - 2010-05-29 13:19:11

QNo.5930015

困ってます

質問者が選んだベストアンサー

1で書いたようにwhileを抜けた後にcountをチェックしてください。
$countが0より大きいということはファイルに書き出されていないデータがあるということです。
ファイルポインタの位置を調べて~云々のことをすればループ内で出力できると思いますが
まぁそこはあまり考えないようにしました。

以下修正(間違ってた箇所があったらゴメンネ)---------------------------------------
define("READ_FILE", "./csv/base_file.csv");

$i = "0"; //総行数カウンター
$count = "0"; //処理件数カウンター
$max = "50"; //ファイルあたりの行数
$file_count = "0"; //ファイル名につける連番
$line=""; //←コレを忘れないように

$RF = fopen( READ_FILE, "r") or die("ファイルが開けません\n");
while (($data = fgetcsv($RF)) !== false) {
$count++;

//1行目はフィールド名だったのをすっかり忘れていたので追加
//2行目から処理
if($i > 0){
//フィールドのデータをいろいろいじるのでsprintf
$lines.= sprintf("%s,%s\n",
$data[0],
$data[1]
);
}
if($count == $max){
//ファイル名生成
$fileno = sprintf("%03d",$file_count);
$filename = "./files/datafile_".$fileno.".dat";
$WF = fopen($filename, "w");
fputs($WF, $lines);
fclose($WF);

//書き込みデータを空にする
$lines = "";
$file_count++;
$count = "0";
}

$i++;
}
if($count == $max){ //←ココでチェックです
//ファイル名生成
$fileno = sprintf("%03d",$file_count);
$filename = "./files/datafile_".$fileno.".dat";
$WF = fopen($filename, "w");
fputs($WF, $lines);
fclose($WF);
}
fclose($RF);

投稿日時 - 2010-05-29 22:32:37

ANo.4

このQ&Aは役に立ちましたか?

10人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

おっと、下のwhile抜け後のcountチェックはif($count > 0){です、すいません。

忘れてましたが1つ目のファイルが1行少なくなるのを防ぐには
if($i > 0){}の中にcount++;を移動してください。
これで「$lineに追加されている行数」という意味合いになります。

投稿日時 - 2010-05-29 22:40:49

ANo.3

本題についてはNo.1、No.2さんが回答して下さっているので、
ちょっとしたアドバイスを。

zerofillという関数を自作されていますが、
通常の0フィルの処理であれば、PHPの組み込み関数で実現可能です。

$fileno = zerofill($file_count);
 ↓
$fileno = sprintf("%04d", $file_count);

これで4桁未満の値の場合は、上位の桁を0で埋めてくれます。
ご参考までに。

投稿日時 - 2010-05-29 15:06:11

お礼

ありがとうございます。

投稿日時 - 2010-05-29 18:42:00

ANo.2

先ほど1で回答したものですが

whileループ内で[$count == $max]となるのは一回前の処理が2000件目だったとき(ループの最後でインクリメントしているため)になります。
そのため2001件目のデータがファイルには保存されないことになってしまいます。
またループに入った際はちょうど2000件出力が行われた場合でもcountは必ず1以上になってしまいます。

そのためループ内の最初でcountをインクリメントする(今処理しているデータはファイルに書き込むn件目という意味合いにする)といいと思います。
また、最初のif($count < $max)は不要($maxに到達した時点で0クリアされるのだから条件は必ず満たす)です。

投稿日時 - 2010-05-29 14:48:55

お礼

ありがとうございます。いただいたアドバイスを元に書き直しました。
サンプルデータの数を減らしてテストをしてみました。
base_file.csv:153行分のテキストファイル

また、すっかり忘れていましたが、1行目はフィールド名なので、必要データは2行目からと
なります。
$count加算の直後に、2行目以上の判定を追加しています。
今回の趣旨とは異なりますが、この場合1つ目のファイルだけは49行なんですよね。
どうしたらいいかわかりませんでしたが、とりあえずデータに抜けはなかったのでよしとします。

ただ、[$count == $max]では、1行目~100行目までしかファイル書き出しができず、
残りの53行分を判定処理する方法がわかりません。
$countが$maxに達したかの判定の次に、残りの分をどうにかする処理を入れるのかと思いますが、
どうしたらいいのでしょうか。

以下サンプルコード---------------------------------------

define("READ_FILE", "./csv/base_file.csv");

$i = "0"; //総行数カウンター
$count = "0"; //処理件数カウンター
$max = "50"; //ファイルあたりの行数
$file_count = "0"; //ファイル名につける連番

$RF = fopen( READ_FILE, "r") or die("ファイルが開けません\n");
while (($data = fgetcsv($RF)) !== false) {
$count++;

//1行目はフィールド名だったのをすっかり忘れていたので追加
//2行目から処理
if($i > 0){
//フィールドのデータをいろいろいじるのでsprintf
$lines.= sprintf("%s,%s\n",
$data[0],
$data[1]
);
}
if($count == $max){
//ファイル名生成
$fileno = sprintf("%03d",$file_count);
$filename = "./files/datafile_".$fileno.".dat";
$WF = fopen($filename, "w");
fputs($WF, $lines);
fclose($WF);

//書き込みデータを空にする
$lines = "";
$file_count++;
$count = "0";
}
//ここでもう一度判定?

$i++;
}

fclose($RF);

投稿日時 - 2010-05-29 19:36:24

ANo.1

whileループ内でcountがmaxに到達しないうちにループを抜ける条件($dataが取得できない)になってしまうための問題です。
whileループを抜けた後にcountが0より大きかったらループ内のif($count == $max)でやっている処理を行うようにしたらどうでしょうか?

投稿日時 - 2010-05-29 13:38:43

あなたにオススメの質問