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返回的资源句柄,最终仅保留最后一个打开的文件句柄,无法读取全部文件内容:
- 写法1:重复给
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
相关产品推荐
相关产品推荐

