使用PHP在短时间内解析大型CSV文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用PHP在短时间内解析大型CSV文件相关的知识,希望对你有一定的参考价值。

我一直在寻找如何在一行中找到一个值并返回CSV文件中另一列的值。

这是我的功能,它工作正常,但在小文件中:

function find_user($filename, $id) {
    $f = fopen($filename, "r");
    $result = false;
    while ($row = fgetcsv($f, 0, ";")) {
        if ($row[6] == $id) {
            $result = $row[5];
            break;
        }
    }
    fclose($f);
    return $result;
}

问题是我必须使用的实际文件大小为4GB。而搜索所需的时间是巨大的。

浏览Stack Overflow,我发现了以下帖子:file_get_contents => PHP Fatal error: Allowed memory exhausted

在那里,他们给了我以下功能(根据我的理解),使我更容易搜索巨大的CSV值:

function file_get_contents_chunked($file,$chunk_size,$callback)
{
    try
    {
        $handle = fopen($file, "r");
        $i = 0;
        while (!feof($handle))
        {
            call_user_func_array($callback,array(fread($handle,$chunk_size),&$handle,$i));
            $i++;
        }

        fclose($handle);

    }
    catch(Exception $e)
    {
         trigger_error("file_get_contents_chunked::" . $e->getMessage(),E_USER_NOTICE);
         return false;
    }

    return true;
}

使用它的方式似乎如下:

$success = file_get_contents_chunked("my/large/file",4096,function($chunk,&$handle,$iteration){
    /*
        * Do what you will with the {&chunk} here
        * {$handle} is passed in case you want to seek
        ** to different parts of the file
        * {$iteration} is the section fo the file that has been read so
        * ($i * 4096) is your current offset within the file.
    */

});

if(!$success)
{
    //It Failed
}

问题是我不知道如何调整我的初始代码以使用凸起的函数来加速大型CSV中的搜索。我在php方面的知识不是很先进。

答案

无论您如何阅读文件,都无法使搜索更快,因为您始终必须在搜索正确的行和列时扫描每个字符。最糟糕的情况是,您要查找的行是文件中的最后一行。

您应该将CSV导入到正确的索引数据库并修改应用程序以进一步将新记录保存到该数据库而不是CSV文件。

这是使用SQLite的基本示例。我创建了一个包含1亿条记录(~5GB)的CSV文件并进行了测试。

创建一个SQLite数据库并将CSV文件导入其中:

$f = fopen('db.csv', 'r');
$db = new SQLite3('data.db');
$db->exec('CREATE TABLE "user" ("id" INT PRIMARY KEY, "name" TEXT,
    "c1" TEXT, "c2" TEXT, "c3" TEXT, "c4" TEXT, "c5" TEXT)');
$stmt = $db->prepare('INSERT INTO "user"
    ("id", "name", "c1", "c2", "c3", "c4", "c5") VALUES (?, ?, ?, ?, ?, ?, ?)');
$stmt->bindParam(1, $id, SQLITE3_INTEGER);
$stmt->bindParam(2, $name, SQLITE3_TEXT);
$stmt->bindParam(3, $c1, SQLITE3_TEXT);
$stmt->bindParam(4, $c2, SQLITE3_TEXT);
$stmt->bindParam(5, $c3, SQLITE3_TEXT);
$stmt->bindParam(6, $c4, SQLITE3_TEXT);
$stmt->bindParam(7, $c5, SQLITE3_TEXT);
$db->exec('BEGIN TRANSACTION');
while ($row = fgetcsv($f, 0, ';')) {
    list($c1, $c2, $c3, $c4, $c5, $name, $id) = $row;
    $stmt->execute();
}
$db->exec('COMMIT');

这需要很长时间,超过15分钟在我的计算机上,导致6.5GB文件。

从数据库中搜索:

$id = 99999999;
$db = new SQLite3('data.db');
$stmt = $db->prepare('SELECT "name" FROM "user" WHERE "id" = ?');
$stmt->bindValue(1, $id, SQLITE3_INTEGER);
$result = $stmt->execute();
print_r($result->fetchArray());

这几乎是瞬间完成的。

以上是关于使用PHP在短时间内解析大型CSV文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 PHP/PDO 将大型 MySQL 表导出为 CSV

使用 json rereiver php mysql 在片段中填充列表视图

我们有许多 EBCDIC 格式的大型机文件,Python 中有没有办法将大型机文件解析或转换为 csv 文件或文本文件?

如何逐行处理大型 CSV 文件?

需要通过 PHP 将大型 CSV 文件导入多个 MySQL 表的高效方法

如何使用 PHP 解析 CSV 文件 [重复]