【问题标题】:fgetcsv is eating the first letter of a String if it's an Umlaut如果它是变音符号,fgetcsv 正在吃字符串的第一个字母
【发布时间】:2012-09-05 15:09:08
【问题描述】:

我正在将 Excel 生成的 CSV 文件中的内容导入 XML 文档,例如:

$csv = fopen($csvfile, r);
$words = array();

while (($pair = fgetcsv($csv)) !== FALSE) {
    array_push($words, array('en' => $pair[0], 'de' => $pair[1]));
}

插入的数据是英语/德语表达。

我将这些值插入到一个 XML 结构中并输出如下 XML:

$dictionary = new SimpleXMLElement('<dictionary></dictionary>');
//do things
$dom = dom_import_simplexml($dictionary) -> ownerDocument;
$dom -> formatOutput = true;

header('Content-encoding: utf-8'); //<3 UTF-8
header('Content-type: text/xml'); //Headers set to correct mime-type for XML output!!!!

echo $dom -> saveXML();

这工作正常,但我遇到了一个非常奇怪的问题。当字符串的第一个字母是元音变音时(如ÖsterreichÄgypten),该字符将被省略,导致gyptensterreich。如果 Umlaut 位于字符串 (Russische Föderation) 的中间,则它会正确传输。 ßé 之类的东西也是如此。

所有文件均采用 UTF-8 编码并以 UTF-8 提供。

这对我来说似乎很奇怪和类似错误,但也许我错过了一些东西,这里有很多聪明人。

【问题讨论】:

  • 是否使用了 ucfirst()?我似乎记得带有变音符号的字符的问题,并且不得不在第一个字符上使用 mb_convert_case()。
  • @jornak 这些值在 CSV 文件中是“正确大写”的,所以我认为我不必搞砸它,但我会试一试。
  • 为什么不用前缀开始这些词呢​​?然后在添加这些单词后将其删除。

标签: php xml csv character-encoding diacritics


【解决方案1】:

可能是某种utf8_encode() 问题。文档页面上的This comment 似乎表明如果您在已经编码时对元音变音进行编码,这可能会导致问题。

也许可以测试一下数据是否已经用mb_detect_encoding() 进行了utf-8 编码。

【讨论】:

  • 我从来没有对这些值做任何编码。问题是 fgetcsv 中的一个错误,请参阅我的答案以获取解决方法。
【解决方案2】:

如果中间的其他元音变音看起来没问题,那么这不是基本编码问题。它发生在行首的事实可能表明与换行符不兼容。也许 CSV 是使用不同的换行编码生成的。

在不同操作系统之间移动文件时会发生这种情况:

  • Windows:\r\n(字符 13 和 10)
  • Linux:\n(字符 10)
  • Mac OS:\r(字符 13)

如果我是你,我会验证换行符以确保。

如果在 Linux 中:hexdump -C filename | more 并检查文档。

如果是这种情况,您可以使用 sed 表达式更改换行符。

希望有所帮助!

【讨论】:

  • 这听起来很合理,但不幸的是,德语 (umlaut-hell) 表达式不在新行的开头,而是在中间。我刚刚将fgetcsv 确定为罪魁祸首,因为当您使用file_get_contents 之类的东西时,内容会被很好地传输。
  • 好的,现在我正在手动处理文件的内容,它工作得很好。我会自己写一个答案。感谢您的意见!
【解决方案3】:

好的,所以这似乎是fgetcsv 中的一个错误。

我现在自己处理 CSV 数据(有点麻烦),但它可以正常工作,而且我根本没有任何编码问题。

这是我正在做的(尚未优化的版本):

$rawCSV = file_get_contents($csvfile);

$lines = preg_split ('/$\R?^/m', $rawCSV); //split on line breaks in all operating systems: http://stackoverflow.com/a/7498886/797194

foreach ($lines as $line) {
    array_push($words, getCSVValues($line));
}

getCSVValues 来自 here,需要处理像这样的 CSV 行(逗号!):

"I'm a string, what should I do when I need commas?",Howdy there

看起来像:

function getCSVValues($string, $separator=","){

    $elements = explode($separator, $string);

    for ($i = 0; $i < count($elements); $i++) {
        $nquotes = substr_count($elements[$i], '"');
        if ($nquotes %2 == 1) {
            for ($j = $i+1; $j < count($elements); $j++) {
                if (substr_count($elements[$j], '"') %2 == 1) { // Look for an odd-number of quotes
                    // Put the quoted string's pieces back together again
                    array_splice($elements, $i, $j-$i+1,
                        implode($separator, array_slice($elements, $i, $j-$i+1)));
                    break;
                }
            }
        }
        if ($nquotes > 0) {
            // Remove first and last quotes, then merge pairs of quotes
            $qstr =& $elements[$i];
            $qstr = substr_replace($qstr, '', strpos($qstr, '"'), 1);
            $qstr = substr_replace($qstr, '', strrpos($qstr, '"'), 1);
            $qstr = str_replace('""', '"', $qstr);
        }
    }
    return $elements;

}

相当多的解决方法,但它似乎工作正常。

编辑:

还有一个 filed bug 用于此,显然这取决于区域设置。

【讨论】:

  • 如果它是 PHP 函数中的错误,您应该在 php.net 上报告它。不过,这也可能对您有所帮助:static.zend.com/topics/multibyte-fgetcsv.pdf
  • 我可以确认这可能是 PHP 中的一个错误,因为我尝试了任何合理的调试方法:stackoverflow.com/questions/16653369/…
  • @JosefSábl 我现在已经遇到过很多次了,发现通过setlocale(LC_ALL,'de_DE.UTF-8')(在我的例子中是德语)设置正确的语言环境设置似乎可以很好地“修补”。
【解决方案4】:

一个更简单的解决方法(但很脏):

//1. replace delimiter in input string with delimiter + some constant
$dataLine = str_replace($this->fieldDelimiter, $this->fieldDelimiter . $this->bugFixer, $dataLine);

//2. parse
$parsedLine = str_getcsv($dataLine, $this->fieldDelimiter);

//3. remove the constant from resulting strings.
foreach ($parsedLine as $i => $parsedField)
{
    $parsedLine[$i] = str_replace($this->bugFixer, '', $parsedField);
}

【讨论】:

    【解决方案5】:

    如果字符串来自 Excel(如果它位于字符串的开头,我遇到了字母 ø 消失的问题)...然后修复它:

    setlocale(LC_ALL, 'en_US.ISO-8859-1');

    【讨论】:

    猜你喜欢
    • 2012-08-11
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多