ajgl/csv-rfc

用于读取和/或写入符合RFC4180标准的CSV文件的PHP原生CSV相关函数的替代方案

0.4.0 2020-12-17 09:47 UTC

This package is auto-updated.

Last update: 2024-09-17 18:08:20 UTC


README

AjglCsvRfc组件提供了PHP原生CSV相关函数的替代方案,用于读取和/或写入符合RFC4180标准的CSV文件。

Build Status Latest Stable Version Latest Unstable Version Total Downloads Montly Downloads Daily Downloads License SensioLabsInsight

原生PHP实现包含一个#50686的“不会修复”错误,当你尝试写入包含转义字符(默认为反斜杠\)后跟封装字符(默认为双引号")的CSV字段时。

RFC 4180规定:

如果使用双引号封装字段,则字段内出现的双引号必须通过前面加另一个双引号进行转义。

字符串“Hello\", World!”的CSV版本应该是“"""Hello\"", World!"”,但它并没有按预期工作。你可以在https://3v4l.org/NnHp4查看详细说明。

此包为以下函数和方法提供了读取和写入良好转义的CSV文件的替代实现:

安装

要安装此组件的最新稳定版本,请打开控制台并执行以下命令

$ composer require ajgl/csv-rfc

使用方法

替代函数

使用此库的最简单方法是调用替代CSV函数

use Ajgl\Csv\Rfc;

$handler = fopen('php://temp', 'w+');
Rfc\fputcsv($handler, array('Hello \"World"!'));
rewind($handler);
$row = Rfc\fgetcsv($handler);
rewind($handler);
$row = Rfc\str_getcsv(fgets($handler));

替代类

如果你更喜欢,可以使用替代实现来替换SplFileObjectSplTempFileObject

use Ajgl\Csv\Rfc;

$file = new Rfc\Spl\SplFileObject('php://temp', 'w+');
$file->fputcsv(array('Hello \"World"!'));
$file->rewind();
$row = $file->fgetcsv();
$file->rewind();
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY);
foreach ($file as $line) {
    $row = $line;
}

流过滤器

除了使用替代函数或类之外,你还可以使用提供的流过滤器来修复封装转义。你必须注册流过滤器(如果尚未注册)并将其附加到流上

use Ajgl\Csv\Rfc;

Rfc\CsvRfcWriteStreamFilter::register();

$handler = fopen('php://temp', 'w+');
stream_filter_append(
    $handler,
    Rfc\CsvRfcWriteStreamFilter::FILTERNAME_DEFAULT,
    STREAM_FILTER_WRITE
);
fputcsv($handler, array('Hello \"World"!'));
rewind($handler);
$row = fgetcsv($handler, 0, ',', '"', '"');

❮ 注意 ❯:在fputcsv中,$escape_char必须是(如果允许)默认值(反斜杠\)。在fgetcsv中,$enclosure$escape参数必须相同。

自定义封装字符

默认情况下,流过滤器的封装字符是双引号(")。如果你想更改它,你可以以两种不同的方式提供自定义封装字符。

通过过滤器参数

可以在将过滤器附加到流时提供一个包含enclosure键的数组

use Ajgl\Csv\Rfc;

$enclosure = '@';
Rfc\CsvRfcWriteStreamFilter::register();

$handler = fopen('php://temp', 'w+');
stream_filter_append(
    $handler,
    Rfc\CsvRfcWriteStreamFilter::FILTERNAME_DEFAULT,
    STREAM_FILTER_WRITE,
    array(
        'enclosure' => $enclosure
    )
);
fputcsv($handler, array('Hello \"World"!'), ',', '@');
rewind($handler);
$row = fgetcsv($handler, 0, ',', '@', '@');
通过过滤器名称

如果过滤器名称以特殊键csv.rfc.write.开头,你可以通过将其附加到过滤器名称来定义自定义封装字符

use Ajgl\Csv\Rfc;

$enclosure = '@';
$filtername = 'csv.rfc.write.' . $enclosure;
Rfc\CsvRfcWriteStreamFilter::register($filtername);

$handler = fopen('php://temp', 'w+');
stream_filter_append(
    $handler,
    $filtername,
    STREAM_FILTER_WRITE
);
fputcsv($handler, array('Hello \"World"!'), ',', '@');
rewind($handler);
$row = fgetcsv($handler, 0, ',', '@', '@');

❮ 注意 ❯:通过参数传递的封装字符将覆盖通过过滤器名称定义的封装字符。

行结束符(EOL)

写入CSV

默认情况下,PHP CSV实现使用LF"\n")作为行结束符来写入CSV行。这些替代函数默认也使用LF"\n")。

但,RFC 4180规定:

每个记录位于单独的一行上,由换行符分隔(CRLF)。

因此,如果你想写入符合RFC4180标准的CSV,你可以使用以下方式覆盖默认的EOL:

use Ajgl\Csv\Rfc\CsvRfcUtils;

CsvRfcUtils::setDefaultWriteEol(CsvRfcUtils::EOL_WRITE_RFC);

读取CSV

为了读取CSV数据,此实现利用PHP的本地功能来读取文件。如果您在EOL检测方面遇到任何问题,应启用auto_detect_line_endings配置选项,如PHP文档建议

ini_set('ini.auto-detect-line-endings', true);

league/csv <9.0集成

众所周知的league/csv包提供了一个出色的面向对象的API来处理CSV数据,但只要它使用默认的PHP CSV实现,版本低于9.0的版本都会受到#50686错误的困扰。

您可以使用此组件与league/csv <9.0一起使用,以生成兼容RFC 4180的文件,从而避免此错误。

写入器集成

要使用Stream Filter API将此组件与league/csv写入器实现集成,您可以使用。

use Ajgl\Csv\Rfc;
use League\Csv\Writer;

CsvRfcWriteStreamFilter::register();
$writer = Writer::createFromPath('/tmp/foobar.csv');
if (!$writer->isActiveStreamFilter()) {
    throw new \Exception("The Stream Filter API is not active.");
}
$writer->appendStreamFilter(CsvRfcWriteStreamFilter::FILTERNAME_DEFAULT);
$writer->insertOne(array('"Hello\", World!'));

❮ 注意 ❯:不要覆盖默认的写入器转义字符(\)。

已知限制
  • 当写入器实例由SplFileObject创建时,league/csv包不支持Stream Filter API。
  • league/csv实现将始终利用标准的\SplFileObject::fputcsv来写入CSV数据,因此从Ajgl\Csv\Rfc\Spl\SplFileObject::fputcsv写入兼容RFC 4180文件的修复将被忽略。

读取器集成

要读取CSV数据,您可以利用标准实现,但您必须将读取器的转义和封装字符设置为相同的值。

use League\Csv\Reader;

$reader = Reader::createFromPath('/tmp/foobar.csv');
$reader->setEscape($reader->getEnclosure());
foreach ($reader as $row) {
    //...
}

许可证

此组件采用MIT许可证。有关完整的许可证信息,请参阅LICENSE文件。

报告问题或功能请求

问题和功能请求在Github问题跟踪器中跟踪。

作者信息

Antonio J. García Lagar开发。

如果您认为此组件很有用,请在其GitHub仓库页面和/或Packagist包页面上添加★。