0

0

PHP如何处理CSV文件_CSV文件读写操作教程

蓮花仙者

蓮花仙者

发布时间:2025-09-20 21:00:01

|

804人浏览过

|

来源于php中文网

原创

答案:PHP处理CSV文件的核心是fgetcsv()和fputcsv()函数,通过流式读写实现高效操作。首先使用fopen()打开文件,读取时结合fgetcsv()逐行解析,写入时用fputcsv()自动处理字段引用,同时注意编码转换(如GBK转UTF-8)、跳过或写入BOM头以兼容Excel,并通过生成器或逐行处理避免大文件内存溢出,最后务必fclose()释放句柄。

php如何处理csv文件_csv文件读写操作教程

PHP处理CSV文件,核心就是利用其内置的文件系统函数,尤其是

fgetcsv()
用于读取,
fputcsv()
用于写入。这套机制相当高效且直接,只要你掌握了文件打开、迭代和关闭的基本逻辑,就能轻松应对绝大部分CSV操作需求。说白了,就是把CSV当成一个结构化的文本文件来处理,一行一行地读写,字段之间用特定分隔符隔开。

解决方案

在PHP中处理CSV文件,无论是读取还是写入,都离不开几个关键步骤和函数。我个人觉得,理解这些函数的参数和返回值,是搞定CSV操作的关键。

1. 读取CSV文件

读取CSV文件通常涉及打开文件、逐行解析、然后关闭文件。

fgetcsv()
是这里的核心。

立即学习PHP免费学习笔记(深入)”;

 Array
        (
            [0] => header1
            [1] => header2
            [2] => header3
        )

    [1] => Array
        (
            [0] => value1
            [1] => value2
            [2] => value with, comma
        )

    [2] => Array
        (
            [0] => another value
            [1] => value4
            [2] => value5
        )

)
*/

?>

fgetcsv()
的第二个参数
length
,我通常设为
0
,这表示不限制行长度,让PHP自己处理,避免了因为行太长而导致数据截断的问题。

2. 写入CSV文件

写入CSV文件与读取类似,也是打开文件、逐行写入、然后关闭。

fputcsv()
是写入的核心。

fputcsv()
会自动处理字段中的分隔符和引号,例如,如果一个字段本身包含逗号,它会自动用双引号将该字段包起来,这省去了我们手动处理的麻烦。

如何处理CSV文件中的编码问题?

说实话,CSV文件的编码问题是我在实际开发中遇到最多的“拦路虎”,尤其是当你需要处理来自不同系统、不同地区(比如国内和国外)的CSV文件时。PHP内部默认是UTF-8,但CSV文件可能来自GBK、ISO-8859-1(Latin-1)或者Windows-1252等编码,一旦编码不匹配,就会出现乱码。

问题根源: CSV文件本身不包含编码信息,它只是纯文本。所以,当你用PHP读取一个非UTF-8编码的CSV文件时,PHP会尝试将其作为UTF-8来解释,结果自然是“天书”。特别是中文环境,GBK编码的CSV非常常见。另一个小麻烦是UTF-8 BOM(Byte Order Mark),它是一个在文件开头用来标识UTF-8编码的特殊字节序列,Excel等软件喜欢它,但PHP的

fgetcsv
可能会把它当作第一个字段的一部分读进去。

解决方案:

  1. 检测编码(尝试性):

    mb_detect_encoding()
    函数可以尝试检测文件的编码,但它并非万能,尤其是对于短小或者内容不够丰富的文本,检测结果可能不准确。不过,作为第一步尝试,它还是有用的。

    $content = file_get_contents($filePath);
    $detectedEncoding = mb_detect_encoding($content, ['UTF-8', 'GBK', 'BIG5', 'EUC-CN', 'CP936', 'Windows-1252', 'ISO-8859-1'], true);
    // 这里的true表示严格模式
    if ($detectedEncoding === false) {
        // 无法检测到,可能需要手动指定或猜测
        error_log("Warning: Could not reliably detect encoding for " . $filePath);
        // 尝试默认GBK或UTF-8
        $detectedEncoding = 'GBK'; // 或者 'UTF-8'
    }
    echo "Detected encoding: " . $detectedEncoding . "\n";
  2. 转换编码: 一旦确定了原始编码,就可以使用

    iconv()
    mb_convert_encoding()
    函数将其转换为PHP内部更方便处理的UTF-8编码。我个人更倾向于
    mb_convert_encoding()
    ,因为它对多字节字符处理得更好。

    示例:读取GBK编码的CSV并转换为UTF-8

  3. 处理UTF-8 BOM: 如前所述,UTF-8 BOM是一个特殊的字节序列

    \xEF\xBB\xBF
    。在读取时,如果CSV文件带有BOM,
    fgetcsv()
    可能会将它作为第一个字段的前缀。最简单的处理方式是在读取文件后,检查第一个字段是否包含BOM,并将其移除。

    // 在读取循环内部,处理第一行数据时
    // $row = fgetcsv($handle, ...);
    if (!empty($row) && isset($row[0])) {
        // 检查并移除BOM
        if (substr($row[0], 0, 3) === "\xEF\xBB\xBF") {
            $row[0] = substr($row[0], 3);
        }
    }

    或者更优雅一点,像我在

    readCsvFile
    函数中那样,在
    fopen
    之后,
    fgetcsv
    之前,直接
    fread
    3个字节来跳过BOM。这通常更可靠。

    ONLYOFFICE
    ONLYOFFICE

    用ONLYOFFICE管理你的网络私人办公室

    下载

大文件CSV如何高效读写,避免内存溢出?

处理大型CSV文件时,最常见的陷阱就是试图一次性将整个文件读入内存。这对于几MB的小文件可能不是问题,但对于几十MB、几百MB甚至更大的文件,很快就会导致PHP脚本内存耗尽("Allowed memory size of X bytes exhausted")。高效读写大型CSV的关键在于“流式处理”——即一次只处理文件的一小部分(通常是一行),而不是全部。

1. 高效读取:

fgetcsv()
的真正力量

我前面展示的

readCsvFile
函数,其实就是一种高效读取的方式。
fgetcsv()
函数本身就是为流式处理设计的。它每次只从文件句柄中读取一行,并将其解析成数组返回。这意味着无论你的CSV文件有多大,
fgetcsv()
在任何给定时间点,都只会在内存中保留当前处理的这一行数据,以及你可能已经积累起来的少量处理结果。

// 再次强调 fgetcsv 的用法,它本身就是高效的
if (($handle = fopen($filePath, 'r')) !== FALSE) {
    // ... BOM处理 ...
    while (($row = fgetcsv($handle, 0, $delimiter, $enclosure)) !== FALSE) {
        // 在这里直接处理 $row,而不是将所有行都存入一个大数组
        // 例如:将数据写入数据库、进行计算、筛选等
        // processRow($row);
        // 如果你需要收集部分数据,也要注意控制收集量,比如每1000行批量处理一次
    }
    fclose($handle);
}

2. 高效写入:逐行输出

与读取类似,写入大文件也应该逐行进行。不要在内存中构建一个巨大的二维数组,然后一次性传给

fputcsv()
的循环。如果你需要将大量数据写入CSV,并且这些数据本身就来源于一个大型数据集(例如数据库查询结果),那么你应该从源头获取一行数据,立即写入CSV,而不是先全部加载到内存。

// 假设 $largeDataSource 是一个可以迭代的大型数据源,比如数据库结果集
function writeLargeCsv(string $filePath, iterable $largeDataSource, string $delimiter = ',', string $enclosure = '"'): bool
{
    if (($handle = fopen($filePath, 'w')) !== FALSE) {
        fwrite($handle, "\xEF\xBB\xBF"); // 写入BOM

        foreach ($largeDataSource as $row) {
            // 确保 $row 是一个数组
            if (is_array($row)) {
                fputcsv($handle, $row, $delimiter, $enclosure);
            } else {
                error_log("Warning: Skipping non-array item from large data source.");
            }
        }
        fclose($handle);
        return true;
    }
    return false;
}

// 示例:从数据库中获取数据并写入CSV
// $stmt = $pdo->query("SELECT name, email, address FROM users WHERE status = 'active'");
// writeLargeCsv('active_users.csv', $stmt); // PDOStatement是可迭代的

3. 利用PHP生成器(Generators)

对于PHP 5.5及更高版本,生成器(

yield
关键字)是处理大型数据集的利器。它可以让你编写看起来像返回一个数组的函数,但实际上它在每次迭代时只“生成”一个值,从而极大地节省内存。

示例:使用生成器读取CSV

 $row) {
//         // 处理每一行数据,内存占用极低
//         // echo "Row " . $rowIndex . ": " . implode(', ', $row) . "\n";
//         // insertIntoDatabase($row);
//     }
// } catch (\RuntimeException $e) {
//     error_log("Error processing CSV: " . $e->getMessage());
// }
?>

使用生成器,你的代码会变得更加简洁和内存友好。你可以在

foreach
循环中直接处理每一行,而无需担心整个文件被加载到内存。

CSV文件处理时常见的陷阱和最佳实践有哪些?

在处理CSV文件时,虽然PHP提供了强大的内置函数,但仍然有一些常见的陷阱需要注意,同时也有一些最佳实践可以遵循,以确保你的代码健壮、高效且不易出错。

常见的陷阱:

  1. 编码问题(老生常谈,但真的太重要了): 我已经强调过多次,这是最让人头疼的问题。如果你不明确CSV文件的编码并进行相应的转换,乱码几乎是必然的。特别是当你的系统需要处理来自不同源的CSV时,这个问题会变得更加复杂。
  2. 不一致的分隔符和引用符: CSV标准本身就有点“模糊”,有些文件用逗号,有些用分号,甚至有些用制表符(Tab-separated values, TSV)。引用符也可能不是双引号。如果你不确定,或者CSV文件格式不一致,
    fgetcsv()
    可能会解析失败,导致数据错位。
  3. 空行或格式错误的行: 有些CSV文件可能包含空行,或者某些行的数据字段数量不匹配。
    fgetcsv()
    在遇到空行时可能会返回
    null
    false
    ,你需要检查这些返回值并决定如何处理(跳过、记录错误等)。
  4. 路径和权限问题: 文件不存在、路径错误、或者PHP脚本没有读写权限,都会导致
    fopen()
    失败。务必检查
    fopen()
    的返回值,并进行错误处理。
  5. 安全漏洞(Excel公式注入): 如果你的CSV文件是提供给用户下载,并且数据来源于用户输入,那么要警惕Excel公式注入攻击。恶意用户可以在字段中插入类似
    =CMD("calc.exe")
    =HYPERLINK("http://malicious.com")
    的公式。当用户在Excel中打开并点击时,可能会执行恶意代码或跳转到恶意链接。务必对用户提供的CSV数据进行清洗和验证,尤其是在写入文件之前。 最简单的方法是,对于任何以
    =
    ,
    +
    ,
    -
    ,
    @
    开头的字段,在其前面加上一个单引号
    '
    ,例如
    '="CMD(...)"
    ,这样Excel会将其视为文本而不是公式。
  6. 忘记关闭文件句柄:
    fopen()
    之后,无论操作成功与否,最终都应该调用
    fclose()
    来释放文件句柄。虽然PHP脚本执行完毕会自动关闭所有打开的文件,但在长时间运行的脚本或循环中,不及时关闭可能导致资源耗尽。

最佳实践:

  1. 明确指定编码和转换: 始终假设CSV文件可能不是UTF-8,并准备好进行编码检测和转换。在写入CSV时,尽量输出UTF-8带BOM的文件,这能提高与Excel等软件的兼容性。
  2. 参数化分隔符和引用符: 在你的读取/写入函数

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

458

2024.03.01

php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

75

2025.12.04

fclose函数的用法
fclose函数的用法

fclose是一个C语言和C++中的标准库函数,用于关闭一个已经打开的文件,是文件操作中非常重要的一个函数,用于将文件流与底层文件系统分离,释放相关的资源。更多关于fclose函数的相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

333

2023.11.30

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

927

2023.09.19

windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

787

2023.07.26

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1129

2023.07.27

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

803

2023.08.01

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号