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

PHP如何合并多个CSV文件后通过fgetcsv逐行读取数据

问题场景
  • 原有单CSV文件读取解析逻辑运行正常,文件打开代码:
$file = fopen ("files/bla1.exp" , "r" );
  • 打开文件后调用fgetcsv按规则逐行解析:长度参数设为0(不限制行长度)、分号为字段分隔符、双引号为字段包裹符,代码如下:
$aCsvLine = fgetcsv($file,"0",";",'"');
  • 需求:当变量$sDate取值为2022-07-18、2022-07-19、2022-07-20、2022-07-21、2022-07-22时,读取多个CSV文件并通过fgetcsv逐行解析全量数据。此前尝试的两种写法均失效:
    • 写法1:重复给$file变量赋值fopen返回的资源句柄,最终仅保留最后一个打开的文件句柄,无法读取全部文件内容:
if($sDate ==  "2022-07-18" || $sDate ==  "2022-07-19" || $sDate ==  "2022-07-20" || $sDate ==  "2022-07-21" || $sDate ==  "2022-07-22")
      {
        $file = fopen ("files/bla1.exp" , "r" );
        $file = fopen ("files/bla2.exp" , "r" );
        $file = fopen ("files/bla3.exp" , "r" );
      }
  • 写法2:通过file_get_contents读取多个文件内容拼接为字符串赋值给$file,不符合fgetcsv要求的文件资源句柄入参类型,无法运行:
$file = '';
if ($sDate ==  "2022-07-18" || $sDate ==  "2022-07-19" || $sDate ==  "2022-07-20" || $sDate ==  "2022-07-21" || $sDate ==  "2022-07-22")
      {
        $file .= file_get_contents("files/bla1.exp");
        $file .= file_get_contents("files/bla2.exp");
        $file .= file_get_contents("files/bla3.exp");
      }
可行实现方案

方案1:循环遍历文件逐个解析(优先选用,内存占用低)

不需要提前合并文件内容,将待读取的文件路径整理为数组,循环打开每个文件,逐行调用fgetcsv解析,单个文件处理完成后关闭句柄再处理下一个,完全兼容原有单行解析逻辑,不受文件大小限制。
实现代码:

// 定义触发多文件读取的日期范围
$targetDates = ["2022-07-18", "2022-07-19", "2022-07-20", "2022-07-21", "2022-07-22"];
// 定义待读取的CSV文件路径列表
$csvFileList = [
    "files/bla1.exp",
    "files/bla2.exp",
    "files/bla3.exp"
];

if (in_array($sDate, $targetDates)) {
    foreach ($csvFileList as $filePath) {
        // 跳过不可读的文件,避免抛出错误
        if (!is_readable($filePath)) {
            continue;
        }
        // 打开当前文件获取句柄
        $handle = fopen($filePath, "r");
        // 逐行解析,参数和原有单文件逻辑完全一致
        while (!feof($handle)) {
            $aCsvLine = fgetcsv($handle, 0, ";", '"');
            // 跳过空行
            if (!$aCsvLine) {
                continue;
            }
            // 此处直接复用原有单行数据处理逻辑即可
        }
        // 处理完成关闭当前文件句柄,释放资源
        fclose($handle);
    }
}

注意:如果CSV文件带有表头行,仅需在处理第一个文件时保留表头解析逻辑,后续文件处理时直接跳过第一行即可,避免重复读取表头数据。

方案2:创建临时流合并内容后统一读取

如果需要保留“先合并全量内容再统一读取”的编码逻辑,可通过PHP内置的php://temp内存/临时文件流写入合并后的内容,重置流指针后即可作为合法句柄传入fgetcsv读取,适合小文件场景。
实现代码:

$targetDates = ["2022-07-18", "2022-07-19", "2022-07-20", "2022-07-21", "2022-07-22"];
$csvFileList = [
    "files/bla1.exp",
    "files/bla2.exp",
    "files/bla3.exp"
];

if (in_array($sDate, $targetDates)) {
    // 创建可读写临时流,内存占用超过5M时自动写入系统临时文件
    $mergedHandle = fopen('php://temp', 'rw+');
    foreach ($csvFileList as $filePath) {
        if (!is_readable($filePath)) {
            continue;
        }
        // 将单个文件内容拷贝到合并流中
        $singleHandle = fopen($filePath, 'r');
        stream_copy_to_stream($singleHandle, $mergedHandle);
        fclose($singleHandle);
    }
    // 将流指针重置到内容起始位置,否则会直接读取到流末尾无数据
    rewind($mergedHandle);

    // 后续解析逻辑和原有单文件逻辑完全一致
    while (!feof($mergedHandle)) {
        $aCsvLine = fgetcsv($mergedHandle, 0, ";", '"');
        if (!$aCsvLine) {
            continue;
        }
        // 此处写入单行数据处理逻辑
    }
    // 关闭合并流释放资源
    fclose($mergedHandle);
}
原有写法问题说明
  • 第一种写法反复覆盖$file变量的句柄值,前序打开的文件句柄未被引用也未关闭,最终变量仅存储最后一次打开的bla3.exp文件句柄,自然无法读取前两个文件的内容。
  • 第二种写法将$file赋值为字符串类型,而fgetcsv第一个参数必须为已打开的文件资源类型句柄,参数类型不匹配直接导致运行失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:39:34