2012-07-18 25 views
1

我想將一個CSV文件導入到我的用Drupal構建的PHP應用程序中。導入從Mozilla Thunderbird導出的CSV文件(我導出聯繫人的地址簿)時遇到了一種奇怪的情況。如果我使用Thunderbird的Windows版本導出,則任何多字節字符都不會呈現在屏幕上,並且在將提取內容的內容轉儲到屏幕時會顯示爲缺少字符。但是,使用使用Linux版本的Thunderbird創建的相同文件時,此問題不存在。在這種情況下,eveything完美地工作。從使用Mozilla Thunderbird創建的CSV文件導入多字節字符到PHP

爲了測試這個,我在Linux和Windows 7上安裝了相同版本的Thunderbird。然後在地址簿中創建相同的單用戶(姓:張,名:利),然後將地址簿導出爲CSV文件。如上所述,linux CSV文件可以成功導入,但Windows不能。

如果我使用file --mime myfilename.csv是得到下面的輸出檢查Linux的文件:

LinuxTB14.csv:text/plain的; charset = utf-8

WinTB14.csv:text/plain; charset = iso-8859-1

所以windows文件,即使它包含中文字符,也被編碼爲iso-8859-1。發現後,我認爲這是一個編碼問題,我只需告訴PHP將違規內容編碼爲UTF-8。

問題是PHP似乎以另一種我無法理解的方式檢測編碼。

// Set correct locale to avoid any issues with multibyte characters. 
$original_local_value = setlocale(LC_CTYPE, 0); 
if ($original_local_value !== 'en_US.UTF-8') { 
    setlocale(LC_CTYPE, 'en_US.UTF-8'); 
} 
$handle = fopen($file->uri, "r"); 
$cardinfo = array(); 
while (($data = fgetcsv($handle, 5000, ",")) !== FALSE) { 
    $cardinfo[] = $data; 
    // dsm() is a drupal function which prints the content of the argument to screen. 
    dsm(mb_detect_encoding($data[0])); 
    dsm($data[0]); 
} 

如果我包括上面的代碼,其示出了編碼和在CSV文件的每行的第一個值的內容,我得到以下渲染到屏幕上:

對於由創建的CSV Thunderbird在窗口

ASCII

UTF-8

對於CSV雷鳥在Linux中創建

ASCII

UTF-8

正如你可以看到PHP正在報告即使Windows文件中的中文字符沒有被打印到屏幕上,也是兩個文件的相同編碼。

任何人有任何想法可能會發生在這裏?

編輯

如果我用記事本打開Windows的CSV文件,並保存爲.. UTF-8格式,那麼該文件將正確導入。所以這顯然是一個編碼問題。如果文件編碼尚未設置爲UTF-8,我添加了以下代碼來轉換文件編碼。

$file_contents = file_get_contents($file->uri); 
    $file_encoding = mb_detect_encoding($file_contents, 'UTF-8, ISO-8859-1, WINDOWS-1252'); 
    if ($file_encoding !== 'UTF-8') { 
    $file_contents = iconv($file_encoding, 'UTF-8', $file_contents); 
    $handle = fopen($file->uri, 'w'); 
    fwrite($handle, $file_contents); 
    fclose($handle); 
    } 

這部分地解決了這個問題。字符出現,但是它們是亂碼(例如張顯示爲張)。我檢查了瀏覽器的頁面編碼和頁面標題,並將它們都設置爲UTF-8,所以它不是瀏覽器問題。

任何想法?

+0

當您在Windows文本編輯器中打開CSV文件時,嘗試使用UTF-8 enconding重新保存它? – nmc 2012-07-18 13:09:02

+0

試圖按照您的建議保存它,並解決了問題。所以看起來我需要在處理之前以編程方式更改文件編碼。 – Benjen 2012-07-19 02:29:40

+0

經過大量的研究,在我看來,沒有可靠的方法來確定適用於所有編碼類型的字符串編碼。鑑於此,我決定不再嘗試檢測和轉換文本文件。相反,我選擇檢測文件是否是UTF-8編碼(可以可靠地檢測到),並在它證明爲假(即不是UTF-8編碼)時返回錯誤消息。 – Benjen 2012-07-23 06:35:57

回答

1

我想出了這個問題的唯一解決方案,不是首先檢測並轉換上傳文件的編碼。經過大量研究,似乎可靠的編碼檢測並不存在。這樣做有太多的錯誤空間。

最安全的選擇是確保上傳的文件以UTF-8編碼,因爲可以可靠地檢測到UTF-8編碼。以下代碼是我如何進行UTF-8編碼檢測。

$file_content = file_get_contents($file->uri); 
// Create regex pattern which detects UTF-8 encoding. 
$regex = '%^(?: 
    [\x09\x0A\x0D\x20-\x7E]    # ASCII 
    | [\xC2-\xDF][\x80-\xBF]    # non-overlong 2-byte 
    | \xE0[\xA0-\xBF][\x80-\xBF]   # excluding overlongs 
    | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte 
    | \xED[\x80-\x9F][\x80-\xBF]   # excluding surrogates 
    | \xF0[\x90-\xBF][\x80-\xBF]{2}  # planes 1-3 
    | [\xF1-\xF3][\x80-\xBF]{3}   # planes 4-15 
    | \xF4[\x80-\x8F][\x80-\xBF]{2}  # plane 16 
)*$%xs'; 
if (!preg_match($regex, $file_content)) { 
    // Not valid UTF-8 encoding so flag an error. 
}