fgetcsv() 丢失带有变音符号(即非ASCII字符)的字符 - 如何修复?

4
类似的问题:
PHP中fgetcsv()无法读取CSV文件中的某些字符
fgetcsv()会忽略行首的特殊字符
我的应用程序有一个表单,用户可以上传CSV文件(它的5个内部用户总是上传有效的文件 - 逗号分隔,带引号,记录以LF结束),然后使用PHP将文件导入数据库。
$fhandle = fopen($uploaded_file,'r');
while($row = fgetcsv($fhandle, 0, ',', '"', '\\')) {
    print_r($row);
    // further code not relevant as the data is already corrupt at this point
}

由于我无法更改原因,用户正在上传使用单字节、8位字符编码的 Windows-1250 字符集的文件。
问题是:在 fgetcsv() 中删除了一些(并非全部!)超过 127 的字符(“扩展ASCII”)。示例数据:
"15","Ústav"
"420","Špičák"
"7","Tmaň"

变成

Array (
  0 => 15
  1 => "stav"
)
Array (
  0 => 420
  1 => "pičák"
)
Array (
  0 => 7
  1 => "Tma"
)

请注意,保留了字母“č”,但省略了字母“Ú”。 fgetcsv 的文档称“自4.3.5版本以来,fgetcsv()现在是二进制安全的”,但看起来它并不是。我做错了什么吗?或者这个函数有问题,我应该寻找其他解析CSV的方法吗?

我会立即回答,这个 bug 之前已经困扰过我,我已经找到了原因;想与 SO 分享,因为这个 bug 很微妙。 - Piskvor left the building
(请澄清,Win-1250只是输入编码。数据稍后会转换并存储为UTF-8;这部分正常工作。最初的数据读取是问题所在。) - Piskvor left the building
1个回答

13

事实证明,我没有仔细阅读文档-fgetcsv()只是有点二进制安全。它对于纯ASCII < 127是安全的,但文档还指出:

  

注意:

     

此函数考虑了区域设置。   如果LANG是例如en_US.UTF-8,则在此   函数中错误地读取一个字节的文件   编码

换句话说,fgetcsv()试图保持二进制安全性,但实际上并不是(因为它同时影响字符集),它可能会破坏它读取的数据(因为这个设置不是在php.ini中配置的,而是从$LANG中读取的)。

我通过使用fgets逐行读取(它逐字节工作,而不是逐字符)并使用文档中的注释所述的CSV函数将它们解析为数组来规避此问题:

$fhandle = fopen($uploaded_file,'r');
while($raw_row = fgets($fhandle)) { // fgets is actually binary safe
    $row = csvstring_to_array($raw_row, ',', '"', "\n");
    // $row is now read correctly
}

错误:fgets长度必须大于0,csvstring_to_array不存在。 - Phil
不是错误:如果没有定义,当然不存在 - 它需要明确定义,它不是内置的。从注释中使用CSV函数以及链接并不清楚? - Piskvor left the building

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接