fgetcsv / fputcsv $ escape参数从根本上打破了

时间:2014-11-10 09:14:53

标签: php csv

概述

fgetcsvfputcsv支持$escape论证,但是,它已被破坏,或者我不理解它应该如何工作。忽略这样一个事实:您没有看到$escape上记录的fputcsv参数,它在PHP源代码中受支持,这是一个小错误,阻止它在文档中出现。

该函数还支持$delimiter$enclosure参数,分别默认为逗号和双引号。我希望传递$escape参数是为了让一个字段包含任何一个元字符(反斜杠,逗号或双引号),但事实并非如此。 (我现在从阅读Wikipedia了解到,这些都用双引号括起来。)

我尝试了什么

例如,fgetcsv文档中评论部分中影响众多海报的陷阱。我们想在字段中写一个反斜杠的情况。

$r = fopen('/tmp/test.csv', 'w');
fwrite($r, '"\"');
fclose($r);

$r = fopen('/tmp/test.csv', 'r');
var_dump(fgetcsv($r));
fclose($r);

返回false。我还尝试了"\\",但也会返回false。用一些含糊不清的文本填充反斜杠会使fgetcsv得到所需的提升... "hi\\there""hi\there"都解析并得到相同的结果,但结果只有1个反斜杠,那么$escape究竟是什么意思呢?

我没有用双引号括起反斜杠时观察到相同的行为。写一个' CSV'包含字符串\\\的文件在由fgetcsv 1 反斜杠进行解析时具有相同的结果。

让我们问PHP如何使用fputcsv

将反斜杠编码为CSV中的字段
$r = fopen('/tmp/test.csv', 'w');
fputcsv($r, array('\\'));
fclose($r);
echo file_get_contents('/tmp/test.csv');

结果是双引号封闭的单个反斜杠(我已经尝试了3个版本的PHP> 5.5.4,当$enclose支持被添加到fputcsv时)。这样的欢闹是fgetcsv根据我上面的笔记甚至无法正确阅读它,它会返回false ...我希望fputcsv不要将fgetcsv括起来双引号中的反斜杠或"\"能够以fputcsv读取fputcsv已将其写入...,或真正在我明显误解的心灵中,{ {1}}写一个带有反斜杠的双引号,并fgetcsv能够正确解析它!

可重复测试

尝试使用fputcsv将单引号写入文件,然后通过fgetcsv阅读。

$aBackslash = array('\\');

// Write a single backslash to a file using fputcsv
$r = fopen('/tmp/test.csv', 'w');
fputcsv($r, $aBackslash);
fclose($r);

// Read the file using fgetcsv
$r = fopen('/tmp/test.csv', 'r');
$aFgetcsv = fgetcsv($r);
fclose($r);

// Compare the read value from fgetcsv to our original value
if(count(array_diff($aBackslash, $aFgetcsv)))
  echo "PHP CSV support is broken\n";

问题

退一步我有一些问题

  • $escape参数的重点是什么?
  • 鉴于CSV文件的定义松散,可以说PHP正在支持正确吗?
  • 什么是正确的'在CSV文件中编码反斜杠的方法吗?

背景

当一位同事为我提供了一个由Python生成的CSV文件时,我最初发现了这一点,该文件写出了一个用双引号括起来的单反斜杠,并且fgetcsv无法读取它。我高兴地问他是否可以使用标准的 Python函数。我几乎不知道PHP CSV工具包是一个纠结的混乱! (FWIW:Python开发人员告诉我他正在使用CSV写入模块)。

2 个答案:

答案 0 :(得分:4)

通过快速浏览一下CSV Format Parameters上的Python文档,封闭值(即双引号内)中使用的转义字符是另一个双引号。

对于PHP,默认转义字符是反斜杠(^);要匹配Python的行为,你需要使用它:

$data = fgetcsv($r, 0, ',', '"', '"');

(^)实际上fgetcsv()以同样的方式处理$enclosure||$enclosure$escape||$enclosure,因此$escape参数用于避免将反斜杠视为特殊性格。

(^^)将$length参数设置为0而不是固定的硬限制会降低效率。

答案 1 :(得分:3)

编辑2

因此,在睡眠之后重新查看代码,结果是fputcsv不接受escape参数,而我是愚蠢的。我已将下面的代码更新为正确的工作代码。适用相同的基本原则,escape参数用于更改escape参数,因此您可以加载带反斜杠的CSV,而不将它们视为转义字符。诀窍是使用不包含在csv中的字符。您可以通过为特定字符抓取文件来执行此操作,直到找到未返回的文件。

修改

好的,所以判断是它检查转义字符,然后永远不会停止检查。所以,如果它找到它,它就会被逃脱。那很简单。

也就是说,escape参数的目的是允许这种情况,你可以将转义字符改为不需要的字符。

我已将您的示例代码转换为正常工作代码:

$aBackslash = array('\\');

// Write a single backslash to a file using fputcsv
$r = fopen('/tmp/test.csv', 'w');
fputcsv($r, $aBackslash, ',', '"'); // EDIT 2: Removed escape param that causes PHP Notice.
fclose($r);

// Read the file using fgetcsv
$r = fopen('/tmp/test.csv', 'r');
$aFgetcsv = fgetcsv($r, ',', '"', '#');
fclose($r);

// Compare the read value from fgetcsv to our original value
if(count(array_diff($aBackslash, $aFgetcsv)))
  echo "PHP CSV support is broken\n";
else
  echo "PHP WORKS!\n";

一个重要的警告是,fgetcsvfputcsv 必须具有相同的参数,否则返回的数组将与原始数组不匹配。

原始答案

你是对的。这是一种失败的语言。我已经尝试过我能想到的所有斜线,我还没有真正实现CSV的成功响应。它总是像你的例子那样返回。

我认为@deceze所提到的是,在你的例子中,你使用array('\\')实际上是字符串文字" \"哪个PHP解释为这样,并传递" \"到CSV,然后以这种方式返回。这会返回错误的回复\",如上所述,这肯定是错误的。

我确实找到了一个解决方案,所以结果实际上是合适的:

首先,对于您的示例,我们需要使用" \"生成/tmp/test.csv。作为身体,或稍微改变阵列。最简单的方法就是将数组更改为:

array('"\\\\"');

之后,我们应该稍微更改fgetcsv请求。

$aFgetcsv = fgetcsv($r);
$aFgetcsv = array_map('stripslashes', $aFgetcsv);

通过这样做,我们告诉PHP剥离第一个斜杠,从而使字符串在$ aFgetcsv" \"