ajgl / csv-rfc
用于读取和/或写入符合RFC4180标准的CSV文件的PHP原生CSV相关函数的替代方案
Requires
- php: ^7.3 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.17
- league/csv: >=7.2,<9.0
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-17 18:08:20 UTC
README
AjglCsvRfc组件提供了PHP原生CSV相关函数的替代方案,用于读取和/或写入符合RFC4180标准的CSV文件。
原生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));
替代类
如果你更喜欢,可以使用替代实现来替换SplFileObject
或SplTempFileObject
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问题跟踪器中跟踪。
作者信息
如果您认为此组件很有用,请在其GitHub仓库页面和/或Packagist包页面上添加★。