读取大文件时出现内存不足错误

时间:2011-12-09 05:28:44

标签: php loops csv error-handling fgetcsv

我有一个大的csv,我想解析并插入到我的数据库中。我有这个PHP:

$target = '../uploads/'.$f;
$handle = fopen($target, "r");
$data = fgetcsv($handle, 0, ",");

$rows = array();

while ($data !== FALSE) {
    $rows[] =  $data;
}

fclose($handle);

if (count($rows)) {
             foreach ($rows as $key => $value) {

                  echo $value;

              }
          }

每次我尝试运行脚本时都会收到此错误:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 35 bytes)

任何想法如何做到这一点?

4 个答案:

答案 0 :(得分:5)

使用mysqlimport代替

虽然您当然可以使用PHP解析和构建查询,但您可以通过让MySQL直接处理它来获得更好的性能。你的数据库会感谢你。

<?php
exec("mysqlimport mysqlimport [options] db_name textfile1");
?>

来源:

答案 1 :(得分:4)

在处理之前,您无需将文件中的所有csv数据读入内存。

相反,创建一个while循环,一次从文件中读取一行。每次从文件中读取时,都应该在数据库中插入一行。

或者读取几行并一次插入几行。

示例:

   $i = 0; 
   while (($data = fgetcsv($handle, 0, ",") !== FALSE) {
        $rows[] = $data;
        $i++;
        // insert 100 rows at one time.
        if ($i % 100 === 0) {
            //insert these rows to db
            insert_to_db($rows);
            //then reset $rows
            $rows = array();
        }
    }
    insert_to_db($rows);

答案 2 :(得分:4)

使用标准的MySQL LOAD DATA INFILE语句来逃避通过PHP读取/解析/插入数据:

 function import_csv( $table, $afields, $filename, $delim = ',', $enclosed = '"', $escaped = '\\',  $lineend = '\\r\\n', $hasheader = FALSE) {      
    if ( $hasheader ) $ignore = "IGNORE 1 LINES ";
    else $ignore = "";
    $q_import = 
        "LOAD DATA INFILE '" . $_SERVER['DOCUMENT_ROOT'] . $filename . "' INTO TABLE " . $table . " " .
        "FIELDS TERMINATED BY '" . $delim . "' ENCLOSED BY '" . $enclosed . "' " .
        "    ESCAPED BY '" . $escaped . "' " .
        "LINES TERMINATED BY '" . $lineend . "' " . $ignore . "(" . implode(',', $afields) . ")"
    ;
    return mysql_query($q_import);
}

在这种情况下,您不需要在PHP中打开/读取CSV文件,MySQL将自行处理数据导入。

答案 3 :(得分:3)

我认为这部分是错误的:

$data = fgetcsv($handle, 0, ",");
$rows = array();
while ($data !== FALSE) {
    $rows[] =  $data;
}

fgetcsv的一次调用会从$handle读取一行。您需要将fgetcsv置于循环条件中:

$handle = fopen($target, "r");
$data = fgetcsv($handle, 0, ",");
while (($row = fgetcsv($handle, 0, ",")) !== FALSE) {
    // Example insert - obviously use prepared statements/escaping/another DAL
    $db->query("INSERT INTO tbl (columns..) VALUES ({$row[0]}, {$row[1]} ... )");
}