clue/reactphp-csv

ReactPHP的流式CSV(逗号分隔值或字符分隔值)解析器和编码器。

v1.2.0 2022-05-13 11:35 UTC

This package is auto-updated.

Last update: 2024-08-31 00:35:33 UTC


README

CI status installs on Packagist

ReactPHP的流式CSV(逗号分隔值或字符分隔值)解析器和编码器。

CSV(逗号分隔值或较少使用的字符分隔值)可以用于在简单的文本文件中存储大量(统一的)记录,例如用户记录列表或日志条目。CSV格式并不是全新的,已经在数十年的大量系统中使用。特别是,CSV经常由于历史原因而被使用,尽管它有缺陷,但仍然是非常常见的导出格式,许多工具都通过它来与电子表格处理器(如Excel、Calc等)进行接口。

  • 标准接口 - 通过实现ReactPHP的标准流接口,可以轻松与现有的高级组件集成。
  • 轻量级,SOLID设计 - 提供了一个薄薄的抽象层,它“足够好”,不会妨碍你。它建立在经过良好测试的组件和已建立的概念之上,而不是重新发明轮子。
  • 良好的测试覆盖率 - 附带一个自动测试套件,并在实际环境中定期测试。

目录

支持我们

我们投入了大量时间来开发、维护和更新我们出色的开源项目。您可以通过成为GitHub的赞助商来帮助我们维持我们工作的高质量。赞助商将获得众多好处,请参阅我们的赞助页面以获取详细信息。

让我们共同努力将这些项目提升到新的水平!🚀

CSV格式

CSV(逗号分隔值或较少使用的字符分隔值)是一种非常简单的文本格式,用于存储大量(统一的)记录,如用户记录列表或日志条目。

Alice,30
Bob,50
Carol,40
Dave,30

虽然这看起来可能有点微不足道,但这种简单性是有代价的。CSV仅限于无类型、二维数据,因此没有标准方式来存储嵌套结构或区分布尔值、字符串或整数。

CSV允许字段名称可选。是否使用字段名称取决于应用程序,因此这个库不会尝试去猜测第一行包含的是字段名称还是字段值。对于许多常见用例,最好像这样包含它们

name,age
Alice,30
Bob,50
Carol,40
Dave,30

CSV允许通过引号将包含空格、分隔逗号或甚至换行符的字段值包围起来进行处理(例如URL或用户提供的描述)

name,comment
Alice,"Yes, I like cheese"
Bob,"Hello
World!"

请注意,这些更高级的解析规则通常由其他应用程序以不一致的方式处理。如今,这些解析规则被定义为RFC 4180的一部分,然而许多应用程序在定义此标准之前就开始使用一些CSV变体。

一些应用程序将CSV称为字符分隔值,仅仅是因为使用另一个分隔符(如分号或制表符)是一种相当常见的做法,这样可以避免在常用值周围使用引号。这在欧洲(及其他地区)使用逗号作为小数分隔符的系统尤其常见。

name;comment
Alice;Yes, I like cheese
Bob;Turn 22,5 degree clockwise

CSV文件通常限于仅ASCII字符,以实现最佳互操作性。然而,许多旧版CSV文件通常使用ISO 8859-1编码或其他变体。较新的CSV文件通常最好以UTF-8保存,因此也可能包含Unicode范围内的特殊字符。文本编码通常是应用程序相关的,因此您最好的选择是始终将其转换为(或假定)UTF-8。

尽管CSV存在不足,但它被广泛使用,这种情况在短期内不太可能改变。特别是,CSV是许多工具与电子表格处理器(如Excel、Calc等)接口的非常常见的导出格式。这意味着CSV通常出于历史原因而被使用,如今使用CSV来存储结构化应用程序数据通常不是一个好主意 - 但将数据导出为CSV以供已知应用程序使用仍然是一个非常合理的做法。

作为替代方案,如果您想以更现代的基于JSON的格式处理结构化数据,您可能想使用clue/reactphp-ndjson来处理换行分隔的JSON(NDJSON)文件(.ndjson文件扩展名)。

{"name":"Alice","age":30,"comment":"Yes, I like cheese"}
{"name":"Bob","age":50,"comment":"Hello\nWorld!"}

作为另一种替代方案,如果您想使用一种避免了一些不足(并且速度稍微快一点)的CSV变体,您可能想使用clue/reactphp-tsv来处理制表符分隔值(TSV)文件(.tsv文件扩展名)。

name	age	comment
Alice	30	Yes, I like cheese
Bob	50	Hello world!

用法

解码器

可以使用Decoder(解析器)类来确保在从流中读取时只返回完整的、有效的CSV元素。它包装了一个给定的ReadableStreamInterface,并通过相同的接口公开其数据,但以解析值的形式发出CSV元素,而不是只是字符串块。

test,1,24
"hello world",2,48
$stdin = new React\Stream\ReadableResourceStream(STDIN);

$csv = new Clue\React\Csv\Decoder($stdin);

$csv->on('data', function (array $data) {
    // $data is a parsed element from the CSV stream
    // line 1: $data = array('test', '1', '24');
    // line 2: $data = array('hello world', '2', '48');
    var_dump($data);
});

ReactPHP的流发出数据字符串块,并且不对它们的长度做出假设。这些块不一定代表完整的CSV元素,因为一个元素可能被分割成多个块。此类通过缓冲不完整的元素来重新组装这些元素。

Decoder支持与底层str_getcsv()函数相同的可选参数。这意味着默认情况下,CSV字段将由逗号(,)分隔,将使用引号封装字符(")和反斜杠转义字符(\)。此行为可以通过可选构造函数参数进行控制。

$csv = new Clue\React\Csv\Decoder($stdin, ';');

$csv->on('data', function (array $data) {
    // CSV fields will now be delimited by semicolon
});

此外,Decoder将最大缓冲区大小(最大行长度)限制为避免由于用户输入不正确导致的缓冲区溢出。通常,没有必要更改此值,除非您知道您正在处理一些不合理的长行。如果需要更改此默认值(64 KiB),它接受一个额外的参数。

$csv = new Clue\React\Csv\Decoder($stdin, ',', '"', '\\', 64 * 1024);

如果底层流发出error事件或普通流包含任何不表示有效CSV流的数据,它将发出一个error事件,然后关闭输入流。

$csv->on('error', function (Exception $error) {
    // an error occurred, stream will close next
});

如果底层流发出end事件,它将清除缓冲区中的任何不完整数据,从而在成功的情况下可能发出一个最终的data事件,然后是一个end事件,或者对于不完整/无效的CSV数据,如上所述,发出一个error事件。

$csv->on('end', function () {
    // stream successfully ended, stream will close next
});

如果底层流或Decoder被关闭,它将转发close事件。

$csv->on('close', function () {
    // stream closed
    // possibly after an "end" event or due to an "error" event
});

close(): void 方法可以用来显式关闭 Decoder 及其底层的流

$csv->close();

pipe(WritableStreamInterface $dest, array $options = array()): WritableStreamInterface 方法可以将所有数据转发到指定的目标流。请注意,Decoder 会发出解码/解析数据事件,而许多(大多数?)可写流只期望数据块

$csv->pipe($logger);

更多详细信息,请参阅 ReactPHP 的 ReadableStreamInterface

关联解码器

AssocDecoder(解析器)类可以确保从流中读取时,您只能接收到完整的、有效的CSV元素。它包装一个给定的 ReadableStreamInterface 并通过相同的接口公开其数据,但将CSV元素作为解析的关联数组发出,而不是仅仅作为字符串块

name,id
test,1
"hello world",2
$stdin = new React\Stream\ReadableResourceStream(STDIN);

$csv = new Clue\React\Csv\AssocDecoder($stdin);

$csv->on('data', function (array $data) {
    // $data is a parsed element from the CSV stream
    // line 1: $data = array('name' => 'test', 'id' => '1');
    // line 2: $data = array('name' => 'hello world', 'id' => '2');
    var_dump($data);
});

是否使用字段名是应用程序相关的,因此此库不尝试 猜测 第一行是否包含字段名或字段值。对于许多常见用例,包含它们并显式使用此类而不是底层的 Decoder 是一个好主意。

实际上,它内部使用 Decoder 类。唯一的区别是,此类要求第一行包含标题名,并将使用此作为所有后续行数据的键,这些数据将作为关联数组发出。在接收到标题名后,此类将始终发出一个包含标题名的 headers 事件。

$csv->on('headers', function (array $headers) {
    // header line: $headers = array('name', 'id');
    var_dump($headers);
});

这意味着输入流必须以一行标题名开始,并且必须为所有记录使用相同的列数。如果输入流不发出任何数据,如果任何行不包含相同数量的列,如果输入流不表示有效的CSV流或如果输入流发出一个 error 事件,此解码器将发出适当的 error 事件并关闭输入流。

此外,此类接受相同的参数,并遵循底层 Decoder 类的完全相同的行为。更多详细信息,请参阅 Decoder 类。

编码器

Encoder(序列化器)类可以确保您写入流中的任何内容最终都会作为有效的CSV元素出现在生成的CSV流中。它包装一个给定的 WritableStreamInterface 并通过相同的接口接受其数据,但将任何数据作为完整的CSV元素处理,而不是仅仅作为字符串块

$stdout = new React\Stream\WritableResourceStream(STDOUT);

$csv = new Clue\React\Csv\Encoder($stdout);

$csv->write(array('test', true, 24));
$csv->write(array('hello world', 2, 48));
test,1,24
"hello world",2,48

Encoder 支持与底层 fputcsv() 函数相同的可选参数。这意味着,默认情况下,CSV字段将由逗号(,)分隔,将使用引号封装字符("),反斜杠转义字符(\),以及Unix样式的EOL(\nLF)。此行为可以通过可选构造函数参数进行控制。

$csv = new Clue\React\Csv\Encoder($stdout, ';');

$csv->write(array('hello', 'world'));
hello;world

如果底层流发出一个 error 事件或给定数据包含任何不能表示为有效CSV流的数据,它将发出一个 error 事件然后 close 输入流。

$csv->on('error', function (Exception $error) {
    // an error occurred, stream will close next
});

如果底层流或 Encoder 被关闭,它将转发 close 事件。

$csv->on('close', function () {
    // stream closed
    // possibly after an "end" event or due to an "error" event
});

end(mixed $data = null): void 方法可以用来可选地发出任何最终数据,然后软关闭 Encoder 及其底层流

$csv->end();

close(): void 方法可以用来显式关闭 Encoder 及其底层流

$csv->close();

有关更多信息,请参阅 ReactPHP 的 WritableStreamInterface

安装

安装此库的推荐方法是 通过 ComposerComposer 是什么?

本项目遵循 SemVer。这将安装最新支持的版本

composer require clue/reactphp-csv:^1.2

有关版本升级的详细信息,请参阅 变更日志

本项目旨在在任何平台上运行,因此不需要任何 PHP 扩展,并支持在从 PHP 5.3 到当前 PHP 8+ 和 HHVM 的旧版 PHP 上运行。强烈建议使用此项目支持的最新 PHP 版本。

测试

要运行测试套件,您首先需要克隆此存储库,然后通过 Composer 安装所有依赖项

composer install

要运行测试套件,请转到项目根目录并运行

vendor/bin/phpunit

许可证

本项目采用宽松的 MIT 许可证

您知道我可以提供定制开发服务和为发布和贡献发放发票吗?有关详细信息,请联系我 (@clue)。

更多信息

  • 如果您想了解更多关于处理数据流的信息,请参阅底层 react/stream 组件的文档。

  • 如果您想以更现代的基于 JSON 的格式处理结构化数据,您可能想使用 clue/reactphp-ndjson 来处理换行符分隔的 JSON (NDJSON) 文件(.ndjson 文件扩展名)。

  • 如果您想处理稍微简单一些的基于文本的表格数据格式,您可能想使用 clue/reactphp-tsv 来处理制表符分隔值 (TSV) 文件(.tsv 文件扩展名)。

  • 如果您想处理压缩的 CSV 文件(.csv.gz 文件扩展名),您可能需要在将解压缩的流传递到 CSV 解码器之前,使用 clue/reactphp-zlib 处理压缩的输入流。

  • 如果您想创建压缩的 CSV 文件(.csv.gz 文件扩展名),您可能需要在将压缩的流传递到文件输出流之前,使用 clue/reactphp-zlib 处理生成的 CSV 编码器输出流。

  • 如果您想并发处理 CSV 流中的记录,您可能想使用 clue/reactphp-flux 来同时处理许多(但不是太多)记录。