2013-01-18 45 views
4

我有一個網站,每月通過FTP接收一個CSV文件一次。多年來,它是一個ASCII文件。現在我在一個月後收到UTF-8,接下來是UTF-16BE和UTF-16LE。也許我會在下個月獲得UTF-32。 Fgets返回UTF文件開頭的字節順序標記。我如何讓PHP自動識別字符編碼?我曾嘗試mb_detect_encoding,無論文件類型如何,它都返回ASCII。我更改了代碼以讀取BOM,並明確地將字符編碼轉換爲mb_convert_encoding。這工作,直到最新的文件,這是UTF-16LE。在這個文件中,它正確地讀取第一行,所有後續行顯示爲問號(「?」)。我究竟做錯了什麼?PHP字符編碼地獄閱讀csv文件與fgets

$fhandle = fopen($file_in, "r"); 
if (fhandle === false) 
    { 
    echo "<p class=redbold>Error opening file $file_in.</p>"; 
    die(); 
    } 

$i = 0; 
while(($line = fgets($fhandle)) !== false) 
{ 
$i++; 

// Detect encoding on first line. Actual text always begins with string "Document" 
if ($i == 1) 
    { 
    $line_start = substr($line, 0, 4); 
    $line_start_hex = bin2hex($line_start); 
    $utf16_start = 'fffe4400'; 
    $utf8_start = 'efbbbf44'; 
    if (strcmp($line_start, 'Docu') == 0) 
     { $char_encoding = 'ASCII'; } 
    elseif (strcmp($line_start_hex, 'efbbbf44') == 0) 
     { 
     $char_encoding = 'UTF-8'; 
     $line = substr($line, 3); 
     } 
    elseif (strcmp($line_start_hex, 'fffe4400') == 0) 
     { 
     $char_encoding = 'UTF-16LE'; 
     $line = substr($line, 2); 
     } 
    elseif (strcmp($line_start_hex, 'feff4400') == 0) 
     { 
     $char_encoding = 'UTF-16BE'; 
     $line = substr($line, 2); 
     } 
    else 
     { 
     echo "<p class=redbold>Error, unknown character encoding. Line =<br>", $line_start_hex, '</p>'; 
     require('../footer.php'); 
     die(); 
     } 
    echo "<p>char_encoding = $char_encoding</p>"; 
    } 

// Convert UTF 
if ($char_encoding != 'ASCII') 
    { 
    $line = mb_convert_encoding($line, 'ASCII', $char_encoding); 
    } 

echo '<p>'; var_dump($line); echo '</p>'; 
} 

輸出:

char_encoding = UTF-16LE 

string(101) "DocumentNumber,RecordedTS,Title,PageCount,City,TransTaxAccountCode,TotalTransferTax,Description,Name 
" 

string(83) "???????????????????????????????????????????????????????????????????????????????????" 

string(88) "????????????????????????????????????????????????????????????????????????????????????????" 

string(84) "????????????????????????????????????????????????????????????????????????????????????" 

string(80) "????????????????????????????????????????????????????????????????????????????????" 

回答

1

我的建議是隻轉換一切UTF-8或ASCII(不完全來自你貼如果你想一切都轉換爲UTF-代碼確定8或ASCII)

$utf8Line = iconv(mb_detect_encoding($line), 'UTF-8', $line); 

或...

$asciiLine = iconv(mb_detect_encoding($line), 'ASCII', $line); 

您可以利用mb_detect_encoding做繁重的你

+0

不幸的是,mb_detect_encoding似乎爲某些UTF文件返回「ASCII」。 – George

+0

哎呀,錯過了那部分問題..回到繪圖板 –

+0

但ascii是unicode的一個子集(第一個255十進制),因此它們應該很容易轉換。只需轉換爲ascii並且不使用多字節字符串。哦,你有沒有想過可能會向提供FTP數據的人大喊大叫? – Amelia

4

明確地傳遞順序和可能的編碼進行檢測,並用嚴格的參數。也 請使用file_get_contents,如果該文件是在UTF-16LE,fgets將擰你。

<?php 
header("Content-Type: text/html; charset=utf-8"); 
$input = file_get_contents($file_in); 

$encoding = mb_detect_encoding($input, array(
    "UTF-8", 
    "UTF-32", 
    "UTF-32BE", 
    "UTF-32LE", 
    "UTF-16", 
    "UTF-16BE", 
    "UTF-16LE" 
), TRUE); 

if($encoding !== "UTF-8") { 
    $input = mb_convert_encoding($input, "UTF-8", $encoding); 
} 
echo "<p>$encoding</p>"; 

foreach(explode(PHP_EOL, $input) as $line) { 
    var_dump($line); 
} 

的順序,因爲UTF-8和UTF-32是更嚴格和UTF-16是非常寬鬆的是重要的;幾乎任何隨機的 甚至字節長度都是有效的UTF-16。

您將保留所有信息的唯一方法是將其轉換爲unicode編碼,而不是ASCII。