将使用Mozilla Thunderbird创建的CSV文件中的多字节字符导入PHP

时间:2012-07-18 08:01:15

标签: php drupal csv thunderbird

我正在尝试将CS​​V文件导入到使用Drupal构建的PHP应用程序中。导入从Mozilla Thunderbird导出的CSV文件时我遇到了一个奇怪的情况(我正在导出联系人的地址簿)。如果我使用Windows版本的Thunderbird导出,则任何多字节字符都不会呈现在屏幕上,并且在将提取的内容的内容转储到屏幕时显示为缺少的字符。但是,使用使用Linux版Thunderbird创建的相同文件时,此问题不存在。在这种情况下,eveything完美无缺。

为了测试这一点,我在Linux和Windows 7上安装了相同版本的Thunderbird。然后在地址簿中创建相同的单个用户(姓:张,名称:利),然后将地址簿导出为CSV文件。如上所述,linux CSV文件可以成功导入,但Windows不能。

如果我使用file --mime myfilename.csv检查linux中的两个文件,请获取以下输出:

  

LinuxTB14.csv:text / plain;字符集= UTF-8

     

WinTB14.csv:text / plain;字符集= 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文件每行中第一个值的编码和内容,我会将以下内容呈现给屏幕:

对于Thunderbird在Windows中创建的CSV

  

ASCII

     

名字

     

UTF-8

对于Linux中由Thunderbird创建的CSV

  

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,因此不是浏览器问题。

有什么想法吗?

1 个答案:

答案 0 :(得分: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.
}