dポイントプレゼントキャンペーン実施中!

行数が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;
}

A 回答 (5件)

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);
    • good
    • 0

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



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

本題についてはNo.1、No.2さんが回答して下さっているので、


ちょっとしたアドバイスを。

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

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

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

ありがとうございます。

お礼日時:2010/05/29 18:42

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



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

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

ありがとうございます。いただいたアドバイスを元に書き直しました。
サンプルデータの数を減らしてテストをしてみました。
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

whileループ内でcountがmaxに到達しないうちにループを抜ける条件($dataが取得できない)になってしまうための問題です。


whileループを抜けた後にcountが0より大きかったらループ内のif($count == $max)でやっている処理を行うようにしたらどうでしょうか?
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!