php如何解析一个包含1500万行数据的csv文件
How to parse a csv file that contains 15 million lines of data in php
我有一个脚本可以解析 CSV 文件并开始验证电子邮件。这适用于 1000 行。但在 1500 万行上它显示内存耗尽错误。文件大小为 400MB。有什么建议么?如何解析和验证它们?
服务器规格:Core i7,32GB Ram
function parse_csv($file_name, $delimeter=',') {
$header = false;
$row_count = 0;
$data = [];
// clear any previous results
reset_parse_csv();
// parse
$file = fopen($file_name, 'r');
while (!feof($file)) {
$row = fgetcsv($file, 0, $delimeter);
if ($row == [NULL] || $row === FALSE) { continue; }
if (!$header) {
$header = $row;
} else {
$data[] = array_combine($header, $row);
$row_count++;
}
}
fclose($file);
return ['data' => $data, 'row_count' => $row_count];
}
function reset_parse_csv() {
$header = false;
$row_count = 0;
$data = [];
}
迭代大型数据集(文件行等)并将其推入数组会增加内存使用量,这与处理的项目数量成正比。
所以文件越大,内存使用量就越大——在这种情况下。
如果需要在处理 CSV 数据之前格式化 CSV 数据的功能,支持它 generators 听起来是个好主意。
阅读 PHP 文档它非常适合你的情况(强调我的):
A generator allows you to write code that uses foreach to iterate over a set of data without needing to build an array in memory, which
may cause you to exceed a memory limit, or require a considerable
amount of processing time to generate.
像这样:
function csv_read($filename, $delimeter=',')
{
$header = [];
$row = 0;
# tip: dont do that every time calling csv_read(), pass handle as param instead ;)
$handle = fopen($filename, "r");
if ($handle === false) {
return false;
}
while (($data = fgetcsv($handle, 0, $delimeter)) !== false) {
if (0 == $row) {
$header = $data;
} else {
# on demand usage
yield array_combine($header, $data);
}
$row++;
}
fclose($handle);
}
然后:
$generator = csv_read('rdu-weather-history.csv', ';');
foreach ($generator as $item) {
do_something($item);
}
这里的主要区别是:
您不会(从内存中)获取并消耗所有数据一次。您按需(如流)获取项目并处理它,一次一个项目。它对内存使用有巨大影响。
P.S.: 上面的 CSV 文件取自: https://data.townofcary.org/api/v2/catalog/datasets/rdu-weather-history/exports/csv
没必要写生成器函数。 SplFileObject 也可以正常工作。
$fileObj = new SplFileObject($file);
$fileObj->setFlags(SplFileObject::READ_CSV
| SplFileObject::SKIP_EMPTY
| SplFileObject::READ_AHEAD
| SplFileObject::DROP_NEW_LINE
);
$fileObj->setCsvControl(';');
foreach($fileObj as $row){
//do something
}
我尝试使用文件 "rdu-weather-history.csv" (> 500KB)。 memory_get_peak_usage() 在 foreach 循环后返回值 424k。值必须逐行处理。
如果创建二维数组,示例所需的存储 space 增加到 8 MB 以上。
您可以尝试的一件事是批量导入 MySQL,这可能会在导入后为您提供更好的工作平台。
LOAD DATA INFILE '/home/user/data.csv' INTO TABLE CSVImport; where CSVimport columns match your CSV.
有点左手边的建议,但根据您的用例,它可能是解析海量数据集的更好方法。
我有一个脚本可以解析 CSV 文件并开始验证电子邮件。这适用于 1000 行。但在 1500 万行上它显示内存耗尽错误。文件大小为 400MB。有什么建议么?如何解析和验证它们?
服务器规格:Core i7,32GB Ram
function parse_csv($file_name, $delimeter=',') {
$header = false;
$row_count = 0;
$data = [];
// clear any previous results
reset_parse_csv();
// parse
$file = fopen($file_name, 'r');
while (!feof($file)) {
$row = fgetcsv($file, 0, $delimeter);
if ($row == [NULL] || $row === FALSE) { continue; }
if (!$header) {
$header = $row;
} else {
$data[] = array_combine($header, $row);
$row_count++;
}
}
fclose($file);
return ['data' => $data, 'row_count' => $row_count];
}
function reset_parse_csv() {
$header = false;
$row_count = 0;
$data = [];
}
迭代大型数据集(文件行等)并将其推入数组会增加内存使用量,这与处理的项目数量成正比。 所以文件越大,内存使用量就越大——在这种情况下。 如果需要在处理 CSV 数据之前格式化 CSV 数据的功能,支持它 generators 听起来是个好主意。
阅读 PHP 文档它非常适合你的情况(强调我的):
A generator allows you to write code that uses foreach to iterate over a set of data without needing to build an array in memory, which may cause you to exceed a memory limit, or require a considerable amount of processing time to generate.
像这样:
function csv_read($filename, $delimeter=',')
{
$header = [];
$row = 0;
# tip: dont do that every time calling csv_read(), pass handle as param instead ;)
$handle = fopen($filename, "r");
if ($handle === false) {
return false;
}
while (($data = fgetcsv($handle, 0, $delimeter)) !== false) {
if (0 == $row) {
$header = $data;
} else {
# on demand usage
yield array_combine($header, $data);
}
$row++;
}
fclose($handle);
}
然后:
$generator = csv_read('rdu-weather-history.csv', ';');
foreach ($generator as $item) {
do_something($item);
}
这里的主要区别是: 您不会(从内存中)获取并消耗所有数据一次。您按需(如流)获取项目并处理它,一次一个项目。它对内存使用有巨大影响。
P.S.: 上面的 CSV 文件取自: https://data.townofcary.org/api/v2/catalog/datasets/rdu-weather-history/exports/csv
没必要写生成器函数。 SplFileObject 也可以正常工作。
$fileObj = new SplFileObject($file);
$fileObj->setFlags(SplFileObject::READ_CSV
| SplFileObject::SKIP_EMPTY
| SplFileObject::READ_AHEAD
| SplFileObject::DROP_NEW_LINE
);
$fileObj->setCsvControl(';');
foreach($fileObj as $row){
//do something
}
我尝试使用文件 "rdu-weather-history.csv" (> 500KB)。 memory_get_peak_usage() 在 foreach 循环后返回值 424k。值必须逐行处理。 如果创建二维数组,示例所需的存储 space 增加到 8 MB 以上。
您可以尝试的一件事是批量导入 MySQL,这可能会在导入后为您提供更好的工作平台。
LOAD DATA INFILE '/home/user/data.csv' INTO TABLE CSVImport; where CSVimport columns match your CSV.
有点左手边的建议,但根据您的用例,它可能是解析海量数据集的更好方法。