ZetCode

PHP CSV

最后修改于 2025 年 2 月 16 日

PHP CSV 教程演示了如何在 PHP 中使用 CSV 数据。

CSV

CSV (逗号分隔值) 是一种非常流行的导入和导出数据格式,用于电子表格和数据库。CSV 文件中的每一行都是一条数据记录。每条记录由一个或多个字段组成,字段之间用逗号分隔。虽然 CSV 是一种非常简单的数据格式,但可能存在许多差异,例如不同的分隔符、换行符或引用字符。

fgetcsv 从给定的文件指针中读取一行,并解析 CSV 字段。它返回一个包含读取字段的数组。fputcsv 接受一个数据数组,并将其作为 CSV 行写入指定的文件句柄。

league/csv 是一个简单的 PHP 库,用于简化 CSV 文档的加载以及编写、选择和转换 CSV 记录。

使用 fgetcsv 读取 PHP CSV

下面的例子使用内置的 fgetcsv 函数来读取 CSV 数据。

users.csv
John,Doe,gardener
Lucy,Smith,teacher
Brian,Bethamy,programmer

这是 users.csv 文件。

read_data.php
<?php

$f = fopen('users.csv', 'r');

while(!feof($f)) {

    $row = fgetcsv($f);

    if (!empty($row)) {
          echo "$row[0] $row[1] is a(n) $row[2]\n";
    }
}

fclose($f);

我们从 users.csv 文件中读取数据。

$f = fopen('users.csv', 'r');

使用 fopen,我们打开一个文件句柄到 users.csv 文件。

while(!feof($f)) {

在一个 while 循环中,我们读取所有行直到文件结束。feof 函数检查文件句柄是否到达文件末尾。

$row = fgetcsv($f);

我们使用 fgetcsv 读取一行;该函数返回一个字段数组。

if (!empty($row)) {
    echo "$row[0] $row[1] is a(n) $row[2]\n";
}

如果该行不为空,我们将在消息中输出这些字段。

fclose($f);

fclose 函数关闭一个打开的文件指针。

$ php read_data.php
John Doe is a(n) gardener
Lucy Smith is a(n) teacher
Brian Bethamy is a(n) programmer

使用 fputcsv 写入 PHP CSV

下面的例子将 CSV 数据写入一个文件。

write_data.php
<?php

$users = [
    ['John', 'Doe', 'gardener' ],
    ['Lucy', 'Smith', 'teacher'],
    ['Brian', 'Bethamy', 'programmer']
];

$fp = fopen('users.csv', 'w');

foreach ($users as $user) {
    fputcsv($fp, $user);
}

fclose($fp);

我们有一个用户数组。我们使用 fputcsv 将用户写入 CSV 文件。

PHP CSV 不同的分隔符

fgetcsv 函数允许读取具有不同分隔符的数据。

users2.csv
John|Doe|gardener
Lucy|Smith|teacher
Brian|Bethamy|programmer

我们的数据使用 | 字符分隔。

separator.php
<?php

$f = fopen('users2.csv', 'r');

while(!feof($f)) {

    $row = fgetcsv($f, 0, '|');

    if (!empty($row)) {
          echo "$row[0] $row[1] is a(n) $row[2]\n";
    }
}

fclose($f);

fgetcsv 函数的第三个参数是可选的分隔符,它设置字段分隔符(仅一个字符)。默认情况下是逗号。

PHP 发送 CSV 数据

下面的例子将 CSV 数据作为附件发送给用户。

send_data.php
<?php

header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=users.csv');

$output = fopen('php://output', 'w');

fputcsv($output, ['First name', 'Last name', 'Occupation']);

$f = fopen('users.csv', 'r');

while (!feof($f)) {

    $rows[] = fgetcsv($f);
}

foreach ($rows as $row) {

    fputcsv($output, $row);
}

fclose($f);

该示例从文件中读取 CSV 并将其返回给用户;用户将该文件作为附件接收。

header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=users.csv');

这些标头指定内容类型和作为附件的处置方式。

$output = fopen('php://output', 'w');

我们创建一个连接到输出流的文件指针。

fputcsv($output, ['First name', 'Last name', 'Occupation']);

我们将标头字段发送到输出流。

$f = fopen('users.csv', 'r');

while (!feof($f)) {

    $rows[] = fgetcsv($f);
}

我们将 CSV 数据读入一个数组。

foreach ($rows as $row) {

    fputcsv($output, $row);
}

该数组被写入输出流。

PHP league\csv

league\csv 是一个用于处理 CSV 数据的 PHP 库。

$ composer require league/csv

该库使用上述命令安装。

首先,我们使用 League\Csv\Reader 解析 CSV 数据进行读取。 League\Csv\Statement 类是一个约束构建器,用于从 League\Csv\Reader 创建的 CSV 文档中选择记录。 Statement::process 方法处理 reader 对象并将找到的记录作为 ResultSet 对象返回。我们可以在结果集上执行过滤、区间或排序操作,就像在 SQL 中一样。

我们使用 League\Csv\Writer 进行写入。

users.csv
'First name','Last name','Occupation'
John,Doe,gardener
Lucy,Smith,teacher
Brian,Bethamy,programmer
Lucy,Black,musician
Pau,Novak,teacher

我们有这些数据。

PHP league\csv 统计行数

在第一个例子中,我们统计 CSV 文件中可用的行数。

count_rows.php
<?php

require __DIR__ . '/vendor/autoload.php';

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('users.csv', 'r');
$rows = Statement::create()->process($reader);
echo count($rows);

我们使用 Reader::createFromPath 创建一个 reader 对象。然后我们创建一个 statement 并使用 process 方法处理 reader 对象。对返回的结果集应用 count 函数将返回行数。

$ php count_rows.php
6

文件中总共有六行,包括标题。

如果我们不想包含标题行,可以使用 setHeaderOffset

skip_header.php
<?php

require __DIR__ . '/vendor/autoload.php';

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('users.csv', 'r');
$reader->setHeaderOffset(0);

$rows = Statement::create()->process($reader);
echo count($rows);

我们统计 CSV 文件中的行数,不包括标题行。

PHP league\csv 读取数据

在下面的例子中,我们使用 league\csv 读取 CSV 数据。

read_data2.php
<?php

require __DIR__ . '/vendor/autoload.php';

use League\Csv\Reader;

$csv = Reader::createFromPath('users.csv', 'r');
$csv->setHeaderOffset(0);

$header = $csv->getHeader();
print_r($header);

$records = $csv->getRecords();
print_r(iterator_to_array($records));

echo $csv->getContent();

我们从 users.csv 文件中读取标题和数据。

$csv = Reader::createFromPath('users.csv', 'r');
$csv->setHeaderOffset(0);

我们创建 reader 对象并设置标题位置。

$header = $csv->getHeader();
print_r($header);

我们获取标题行。

$records = $csv->getRecords();
print_r(iterator_to_array($records));

getRecords 方法将所有 CSV 记录作为 Iterator 对象返回。

echo $csv->getContent();

getContent 方法将 CSV 文档作为字符串返回。

$ php read_data2.php
Array
(
    [0] => 'First name'
    [1] => 'Last name'
    [2] => 'Occupation'
)
Array
(
    [1] => Array
        (
            ['First name'] => John
            ['Last name'] => Doe
            ['Occupation'] => gardener
        )

    [2] => Array
        (
            ['First name'] => Lucy
            ['Last name'] => Smith
            ['Occupation'] => teacher
        )

    [3] => Array
        (
            ['First name'] => Brian
            ['Last name'] => Bethamy
            ['Occupation'] => programmer
        )

    [4] => Array
        (
            ['First name'] => Lucy
            ['Last name'] => Black
            ['Occupation'] => musician
        )

    [5] => Array
        (
            ['First name'] => Pau
            ['Last name'] => Novak
            ['Occupation'] => teacher
        )

)
'First name','Last name','Occupation'
John,Doe,gardener
Lucy,Smith,teacher
Brian,Bethamy,programmer
Lucy,Black,musician
Pau,Novak,teacher

PHP league/csv fetchColumn

可以使用 fetchColumn 选择特定的数据列。

fetch_column.php
<?php

require __DIR__ . '/vendor/autoload.php';

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('users.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader);

foreach ($records->fetchColumn(2) as $value) {
    echo $value . "\n";
}

在示例中,我们从数据中获取第三列。

$ php fetch_column.php
gardener
teacher
programmer
musician
teacher

Slim 中的 PHP CSV

在下面的例子中,我们从一个 Slim 应用程序返回 CSV 数据。

$ composer req slim/slim
$ composer req slim/psr7
$ composer req slim/http

我们安装 slim/slimslim/psr7slim/http 包。

public/index.php
<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Psr7\Stream;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/users', function (Request $request, Response $response): Response {

    $csv_file = '../users.csv';
    $fp = fopen($csv_file);
    $stream = new Stream(fopen($csv_file, 'rb'));

    return $response->withHeader('Content-Type', 'application/octet-stream')
        ->withHeader('Content-Disposition', 'attachment; filename=users.csv')
        ->withHeader('Pragma', 'no-cache')
        ->withBody($stream);
});

$app->run();

我们从文件中读取数据,并将其发送到响应主体中。

$ php -S localhost:8000 -t public

我们启动内置服务器。

$ curl localhost:8000/users
"First name","Last name",Occupation
John,Doe,gardener
Lucy,Smith,teacher
Brian,Bethamy,programmer

使用 curl 创建一个 GET 请求。

Symfony 中的 PHP CSV

在下面的例子中,我们从一个 Symfony 应用程序发送一个 CSV 响应。

$ symfony new symcsv
$ cd symcsv

创建一个新项目。

$ composer req maker --dev
$ composer req annot

我们安装 makerannot 依赖项。

$ php bin/console make:controller UserController

我们创建 UserController

src/Controller/UserController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Service\ContainerParametersHelper;

class UserController extends AbstractController
{
    /**
     * @Route("/users", name="user")
     */
    public function index(): Response
    {
        $rootDir = $this->getParameter('kernel.project_dir');

        $fp = fopen("$rootDir/var/users.csv", 'r');
        $response = new Response(stream_get_contents($fp));
        fclose($fp);

        $response->headers->set('Content-Type', 'text/csv');
        $response->headers->set('Pragma', 'no-cache');
        $response->headers->set('Content-Disposition',
            'attachment; filename="users.csv"');

        return $response;
    }
}

users.csv 文件位于 var 目录中。我们读取文件的内容,并将其作为附件发送到响应对象中。

$ symfony serve

我们启动 Web 服务器。

$ curl localhost:8000/users
"First name","Last name",Occupation
John,Doe,gardener
Lucy,Smith,teacher
Brian,Bethamy,programmer

我们使用 curl 发送一个 GET 请求。

Laravel 中的 PHP CSV

在下面的例子中,我们从一个 Laravel 应用程序发送一个 CSV 响应。

$ laravel new laracsv
$ cd laracsv

创建一个新项目。

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Symfony\Component\HttpFoundation\StreamedResponse;

Route::get('/users', function() : StreamedResponse {

    $headers = [
        "Content-type"        => "text/csv",
        "Content-Disposition" => "attachment; filename=users.csv",
        "Pragma"              => "no-cache",
    ];

    $columns = ['First name', 'Last name', 'Occupation'];
    $fileName = storage_path('users.csv');
    $f = fopen($fileName, 'r');

    while (!feof($f)) {

        $rows[] = fgetcsv($f);
    }

    fclose($f);

    return new StreamedResponse(

        function() use ($rows) {

            $handle = fopen('php://output', 'w');

            foreach ($rows as $row) {

                if (!empty($row)) {
                    fputcsv($handle, $row);
                }
            }

            fclose($handle);
        },
        200,
        [
            'Content-type'        => 'text/csv',
            'Content-Disposition' => 'attachment; filename=members.csv'
        ]
    );
});

users.csv 文件位于 storage 目录中。我们读取文件的内容,并将其作为附件发送到响应对象中。

$ php artisan serve

我们启动 Web 服务器并定位到 localhost:8000/users

来源

fgetcsv - PHP 手册

在本文中,我们使用纯 PHP、Symfony、Slim 和 Laravel 处理 CSV 数据。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有 PHP 教程。