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

问题描述 投票:2回答:1

我一直在寻找如何在一行中找到一个值并返回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方面的知识不是很先进。

php database csv parsing fgetcsv
1个回答
3
投票

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

您应该将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());

这几乎是瞬间完成的。

© www.soinside.com 2019 - 2024. All rights reserved.