You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP分批读取大CSV文件(20万条)及指定行范围读取问题

PHP 高效分批读取大CSV文件的正确实现

我太懂处理超大CSV的痛苦了——20万条记录逐条插入简直是噩梦,分批读取+批量插入才是正确的打开方式。你之前尝试的跳过方法失效,主要是因为fgets没法正确识别CSV中带换行的字段(比如被引号包裹的多行内容),导致文件指针跑偏了。下面给你一套靠谱的实现方案:

核心思路

利用fgetcsv严格按照CSV规范解析每一行,每次读取指定批次的记录(比如10000条),处理完一批后继续读取下一批,直到文件结束。同时配合数据库批量插入操作,能把效率提升N倍。

完整代码示例

<?php
// 配置参数
$csvFilePath = 'your_large_file.csv';
$batchSize = 10000; // 每批次读取的记录数
$delimiter = ','; // CSV分隔符,根据你的文件实际情况调整

// 打开CSV文件
$handle = fopen($csvFilePath, 'r');
if (!$handle) {
    die("无法打开目标CSV文件");
}

// 读取表头(如果你的CSV包含表头行的话,不需要可以注释掉)
$header = fgetcsv($handle, 0, $delimiter);

// 循环分批读取并处理
while (!feof($handle)) {
    $batchData = [];
    
    // 读取当前批次的记录
    for ($i = 0; $i < $batchSize; $i++) {
        $row = fgetcsv($handle, 0, $delimiter);
        if (!$row) {
            break; // 文件已读完,跳出当前批次循环
        }
        // 可选:将行数据与表头关联成键值对数组,方便后续处理
        $batchData[] = array_combine($header, $row);
        // 如果不需要表头关联,直接存原始数组即可:$batchData[] = $row;
    }
    
    // 处理当前批次的数据(比如批量插入数据库)
    if (!empty($batchData)) {
        processBatch($batchData);
        echo "已完成 " . count($batchData) . " 条记录的处理\n";
    }
}

fclose($handle);
echo "所有记录处理完成!";

/**
 * 批量处理数据的示例函数(以MySQL批量插入为例)
 * @param array $batchData 待处理的批次数据
 */
function processBatch($batchData) {
    // 替换为你的数据库连接信息
    $pdo = new PDO('mysql:host=localhost;dbname=your_database;charset=utf8mb4', 'username', 'password');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    // 关闭自动提交,用事务包裹批量操作,提升插入效率
    $pdo->beginTransaction();
    
    // 构造批量插入SQL语句(根据你的表结构调整字段)
    $fields = implode(', ', array_keys($batchData[0]));
    $valuePlaceholders = rtrim(str_repeat('(' . implode(', ', array_fill(0, count($batchData[0]), '?')) . '), ', count($batchData)), ', ');
    $sql = "INSERT INTO your_table ($fields) VALUES $valuePlaceholders";
    
    $stmt = $pdo->prepare($sql);
    
    // 扁平化数据数组,适配PDO的execute参数要求
    $flatData = [];
    foreach ($batchData as $row) {
        $flatData = array_merge($flatData, array_values($row));
    }
    
    $stmt->execute($flatData);
    
    // 提交事务
    $pdo->commit();
}
?>

关键细节说明

  1. 为什么不用fgets跳过行?
    CSV文件中经常会出现带引号的字段包含换行的情况,fgets只会按换行符拆分内容,无法识别这种合法的CSV行结构,导致文件指针定位错误。而fgetcsv会严格遵循CSV规范解析每一行,确保指针位置始终正确。

  2. 批次大小的调整
    10000条是比较均衡的数值,你可以根据服务器内存和数据库性能灵活调整:内存充足的话,适当增大批次能减少数据库交互次数;内存紧张则调小批次,避免内存溢出。

  3. 数据库效率优化

    • 一定要用批量插入语句(比如代码中的多值INSERT),而不是逐条执行INSERT,这是提升插入效率的核心。
    • 用事务包裹批量操作,关闭自动提交,能大幅减少磁盘IO的次数。
    • 如果是MySQL,还可以临时调整innodb_flush_log_at_trx_commit=2(测试环境)或增大bulk_insert_buffer_size参数进一步优化。
  4. 解决你之前的问题
    你之前尝试的手动跳过行的思路,本质是想通过指针定位跳过前面的记录,但因为fgets不识别CSV的复杂行结构,导致指针跳错位置。上面的代码不需要手动跳过,而是自然循环读取每一批,直到文件结束,完全避免了指针定位的问题。

内容的提问来源于stack exchange,提问作者user9288207

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:35:48