5

我正在处理 php 中的 csv 导入脚本。它工作正常,除了字段开头的外来字符。

代码看起来像这样

if (($handle = fopen($filename, "r")) !== FALSE)
{
     while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) 
         $teljing[] = $data;

     fclose($handle);
}

这是一个显示我的问题的数据示例

føroyskir stavir, "Kr. 201,50"
óvirkin ting, "Kr. 100,00"

这将导致以下结果

array 
(
     [0] => array 
          (
                 [0] => 'føroyskir stavir',
                 [1] => 'Kr. 201,50'
          )
     [1] => array 
          (
                 [0] => 'virkin ting', <--- Should be 'óvirkin ting'
                 [1] => 'Kr. 100,00'
          )
)

我在 php.net 的一些评论中看到了这种行为,并且我试图ini_set('auto_detect_line_endings',TRUE);检测行尾。没有成功。

有谁熟悉这个问题?

编辑:

谢谢AJ,这个问题现在已经解决了。

setlocale(LC_ALL, 'en_US.UTF-8');

是解决方案。

4

2 回答 2

6

来自PHP手册fgetcsv()

“注意:此功能考虑了区域设置。如果 LANG 是例如 en_US.UTF-8,则此功能会读取单字节编码的文件错误。”

于 2011-04-22T14:43:05.000 回答
0

复制自 PHP.net/fgetcsv 评论:

kent at marketruler dot com 04-Feb-2010 11:18 请注意,至少在 PHP 5.3 或更早版本中,fgetcsv 不能用于 UTF-16 编码文件。您的选择是将整个文件转换为 ISO-8859-1(或 latin1),或逐行转换并将每一行转换为 ISO-8859-1 编码,然后使用 str_getcsv(或兼容的向后兼容实现)。如果您需要阅读非拉丁字母,最好转换为 UTF-8。

请参阅 str_getcsv 了解它与 PHP < 5.3 的向后兼容版本,并参阅 utf8_decode 了解 Rasmus Andersson 编写的提供 utf16_decode 的函数。我添加的修改是 BOP 出现在文件的顶部,然后不在后续行中。因此,您需要存储字节序,然后在每个后续行解码时重新发送。如果不可用,此修改后的版本将返回字节顺序:

<?php
/**
 * Decode UTF-16 encoded strings.
 *
 * Can handle both BOM'ed data and un-BOM'ed data.
 * Assumes Big-Endian byte order if no BOM is available.
 * From: http://php.net/manual/en/function.utf8-decode.php
 *
 * @param   string  $str  UTF-16 encoded data to decode.
 * @return  string  UTF-8 / ISO encoded data.
 * @access  public
 * @version 0.1 / 2005-01-19
 * @author  Rasmus Andersson {@link http://rasmusandersson.se/}
 * @package Groupies
 */
function utf16_decode($str, &$be=null) {
    if (strlen($str) < 2) {
        return $str;
    }
    $c0 = ord($str{0});
    $c1 = ord($str{1});
    $start = 0;
    if ($c0 == 0xFE && $c1 == 0xFF) {
        $be = true;
        $start = 2;
    } else if ($c0 == 0xFF && $c1 == 0xFE) {
        $start = 2;
        $be = false;
    }
    if ($be === null) {
        $be = true;
    }
    $len = strlen($str);
    $newstr = '';
    for ($i = $start; $i < $len; $i += 2) {
        if ($be) {
            $val = ord($str{$i})   << 4;
            $val += ord($str{$i+1});
        } else {
            $val = ord($str{$i+1}) << 4;
            $val += ord($str{$i});
        }
        $newstr .= ($val == 0x228) ? "\n" : chr($val);
    }
    return $newstr;
}
?>

Trying the "setlocale" trick did not work for me, e.g.

<?php
setlocale(LC_CTYPE, "en.UTF16");
$line = fgetcsv($file, ...)
?>

但这可能是因为我的平台不支持它。但是,fgetcsv 仅支持分隔符的单个字符等,并且如果您传入所述字符的 UTF-16 版本,则会抱怨,所以我很快就放弃了。

希望这对那里的人有帮助。

于 2011-04-22T14:42:51.197 回答