使用fgetcsv读取CSV文件时遇到UTF-8问题

39

我尝试读取CSV文件并输出其内容。但是内容显示的字符不正确。

Mäx Müstermänn -> Mäx Müstermänn

CSV文件的编码为UTF-8而没有BOM(使用Notepad++检查)。

这是CSV文件的内容:

"Mäx";"Müstermänn"

我的PHP脚本:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<?php
$handle = fopen ("specialchars.csv","r");
echo '<table border="1"><tr><td>First name</td><td>Last name</td></tr><tr>';
while ($data = fgetcsv ($handle, 1000, ";")) {
        $num = count ($data);
        for ($c=0; $c < $num; $c++) {
            // output data
            echo "<td>$data[$c]</td>";
        }
        echo "</tr><tr>";
}
?>
</body>
</html>

我尝试使用 setlocale(LC_ALL, 'de_DE.utf8');,如此处所建议的,但并没有成功。内容仍然显示错误。

我缺少了什么吗?

编辑:

echo mb_detect_encoding($data[$c],'UTF-8'); 显示 UTF-8 UTF-8。

echo file_get_contents("specialchars.csv"); 显示 "Mäx";"Müstermänn"

print_r(str_getcsv(reset(explode("\n", file_get_contents("specialchars.csv"))), ';'))

给我

数组([0]=> Mäx [1]=> Müstermänn)

这是什么意思?


当您执行echo file_get_contents("specialchars.csv")时会发生什么?当您执行print_r(str_getcsv(reset(explode("\n", file_get_contents("specialchars.csv"))), ';'))时会发生什么? - Furgas
6个回答

76

试试这个:

<?php
$handle = fopen ("specialchars.csv","r");
echo '<table border="1"><tr><td>First name</td><td>Last name</td></tr><tr>';
while ($data = fgetcsv ($handle, 1000, ";")) {
        $data = array_map("utf8_encode", $data); //added
        $num = count ($data);
        for ($c=0; $c < $num; $c++) {
            // output data
            echo "<td>$data[$c]</td>";
        }
        echo "</tr><tr>";
}
?>

13
这完全移除了带有空格的特殊字符,这非常危险!!! - Clain Dsilva
3
@robssanches 上面的代码只适用于字母类型的单词(字符),但它不适用于其他语言,例如中文、印地语、希伯来语等。 - Sachin Sarola
这对我很有帮助。很遗憾,这个有用的代码在官方文档http://de.php.net/manual/de/function.fgetcsv.php中缺失。 - Peter
我在这个解决方案上遇到了一些问题... 一些字符,如’(右单引号)和…(省略号),无法与utf8_encode一起使用。 - Loenix

18

遇到了类似的问题:解析包含特殊字符如é、è、ö等的CSV文件...

以下方法适用于我:

为了在HTML页面上正确表示这些字符,需要添加以下头部:

header('Content-Type: text/html; charset=UTF-8');
为了正确解析每个字符,我使用了:
utf8_encode(fgets($file));
不要忘记在所有字符串操作中使用“多字节字符串函数”,例如:
mb_strtolower($value, 'UTF-8');

你刚刚帮我省了很多时间,谢谢!我已经试图解决这个问题很长时间了。 - Kiss Koppány
1
一个完整的示例代码,其中实际使用了 utf8_encode(fgets($file));,将是很好的。 - Robert Sinclair

9

在我的情况下,源文件采用windows-1250编码,使用iconv时会输出关于输入字符串中非法字符的大量提示...

因此,这个解决方案对我非常有帮助:

/**
 * getting CSV array with UTF-8 encoding
 *
 * @param   resource    &$handle
 * @param   integer     $length
 * @param   string      $separator
 *
 * @return  array|false
 */
private function fgetcsvUTF8(&$handle, $length, $separator = ';')
{
    if (($buffer = fgets($handle, $length)) !== false)
    {
        $buffer = $this->autoUTF($buffer);
        return str_getcsv($buffer, $separator);
    }
    return false;
}

/**
 * automatic convertion windows-1250 and iso-8859-2 info utf-8 string
 *
 * @param   string  $s
 *
 * @return  string
 */
private function autoUTF($s)
{
    // detect UTF-8
    if (preg_match('#[\x80-\x{1FF}\x{2000}-\x{3FFF}]#u', $s))
        return $s;

    // detect WINDOWS-1250
    if (preg_match('#[\x7F-\x9F\xBC]#', $s))
        return iconv('WINDOWS-1250', 'UTF-8', $s);

    // assume ISO-8859-2
    return iconv('ISO-8859-2', 'UTF-8', $s);
}

回应@manvel的答案 - 使用str_getcsv而不是explode - 因为有这样的情况:

some;nice;value;"and;here;comes;combinated;value";and;some;others

explode函数可以将字符串分割成多个部分:

some
nice
value
"and
here
comes
combinated
value"
and
some
others

但是,str_getcsv函数会将字符串分解为多个部分:

some
nice
value
and;here;comes;combinated;value
and
some
others

太好了!这是唯一一个在使用PHP操作CSV数据时处理错误字符编码问题的答案。要么在操作之前正确编码数据,否则在读取时进行即时编码。在我的情况下,fgetcsv返回了损坏的输出(没有任何PHP通知,甚至没有NULL或FALSE!),因为存在编码错误。你用 fgetcsvUTF8 为我节省了宝贵的时间,因为我无法重新编码原始数据,我讨厌编码问题...感谢分享! - EricLavault
这个非常有效。我遇到了一个用例它不起作用。不确定您是否有任何想法: 奥兰群岛 - 使用您的函数将返回具有该文本的行 ?land Islands。除此之外,我没有发现任何问题。 - Lawrence Johnson
谢谢您的回答。请描述一下您是如何解决这个问题的。 - Petr Hladík

8

请将以下内容放在文件顶部(在任何其他输出之前):

<?php

header('Content-Type: text/html; charset=UTF-8');

?>

1
如果我把这个放在顶部,我会得到�。 - testing
1
也许我应该提一下,我通过一个具有 enctype="multipart/form-data" accept-charset="utf-8" 属性的表单上传了 CSV 文件。如果我把你的代码放进示例中,那么它似乎可以工作。 - testing
@测试对我产生了影响。 我有2个CSV文件要解析,一个带有accept-charset ="utf-8",另一个没有,直到我使用它才能正确显示。 - AutoBaker

5
问题在于该功能返回UTF-8编码(可以使用mb_detect_encoding检查),但不进行转换,并将这些字符视为UTF-8编码。因此,需要使用iconv将其逆转换为初始编码(Windows-1251或CP1251)。由于fgetcsv返回一个数组,建议编写自定义函数: [对我的英语表示抱歉]
function customfgetcsv(&$handle, $length, $separator = ';'){
    if (($buffer = fgets($handle, $length)) !== false) {
        return explode($separator, iconv("CP1251", "UTF-8", $buffer));
    }
    return false;
}

2
现在我已经解决了问题(删除 header 命令后)。 我认为问题是php文件的编码为ISO-8859-1。 我将其设置为UTF-8而没有BOM。 我以为我已经这样做了,但可能我又做了一次撤销。
此外,我为数据库使用了 SET NAMES 'utf8'。 现在数据库中也是正确的。

如果导入的文件字符集与您的代码不同,您可能还需要使用setlocale()。 - tim

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