如何使用PHP从CSV文件读取多字节字符
问题描述:
我有一个包含英文和中文字符(它是从Mozilla Thunderbird电子邮件程序导出的联系人列表)的CSV文件。我试图创建一个可以从这个文件中提取信息的函数。看来函数fgetcsv()不支持多字节字符。由于我运行的是PHP5.2,因此我无法访问str_getcsv()。如何使用PHP从CSV文件读取多字节字符
虽然上面的情况指的是英文和中文,但我正在寻找一种适用于任何语言的解决方案。
现在我有函数namecards_import_str_getcsv()作为我的CSV解析函数,它试图模仿str_getcsv()。
$file = $_SESSION['namecards_csv_file'];
if (file_exists($file->uri)) {
// Load raw csv content into a handler variable.
$handle = fopen($file->uri, "r");
$cardinfo = array();
while (($data = fgets($handle)) !== FALSE) {
$data = namecards_import_str_getcsv($data);
dsm($data);
$cardinfo[] = $data[0];
}
fclose($handle);
}
else {
drupal_set_message(t('CSV file doesn\'t exist'), 'error');
}
在结果阵列中国字符的字符串是在所述阵列中的正确位置由它们例如显示为符号:
function namecards_import_str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
if (!function_exists('str_getcsv')) {
if (is_string($input) && !empty($input)) {
$output = array();
$tmp = preg_split("/".$eol."/",$input);
if (is_array($tmp) && !empty($tmp)) {
while (list($line_num, $line) = each($tmp)) {
if (preg_match("/" . $escape . $enclosure . "/", $line)) {
while ($strlen = strlen($line)) {
$pos_delimiter = strpos($line, $delimiter);
$pos_enclosure_start = strpos($line, $enclosure);
if (is_int($pos_delimiter) && is_int($pos_enclosure_start) && ($pos_enclosure_start < $pos_delimiter)) {
$enclosed_str = substr($line, 1);
$pos_enclosure_end = strpos($enclosed_str, $enclosure);
$enclosed_str = substr($enclosed_str, 0, $pos_enclosure_end);
$output[$line_num][] = $enclosed_str;
$offset = $pos_enclosure_end + 3;
}
else {
if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
$output[$line_num][] = substr($line, 0);
$offset = strlen($line);
}
else {
$output[$line_num][] = substr($line,0,$pos_delimiter);
$offset = (!empty($pos_enclosure_start) && ($pos_enclosure_start < $pos_delimiter))? $pos_enclosure_start : $pos_delimiter + 1;
}
}
$line = substr($line,$offset);
}
}
else {
$line = preg_split("/" . $delimiter . "/", $line);
/*
* Validating against pesky extra line breaks creating false rows.
*/
if (is_array($line) && !empty($line[0])) {
$output[$line_num] = $line;
}
}
}
return $output;
}
else {
return false;
}
}
else {
return false;
}
}
else {
return str_getcsv($input);
}
}
这个功能是通过下面的行的代码调用“С”。
在此之前我尝试过的另一种方法是简单地使用fgetcsv()(请参见下面的示例)。但在这种情况下,返回数组的元素是空的。
$file = $_SESSION['namecards_csv_file'];
if (file_exists($file->uri)) {
// Load raw csv content into a handler variable.
$handle = fopen($file->uri, "r");
$cardinfo = array();
while (($data = fgetcsv($handle, 5000, ",")) !== FALSE) {
dsm($data);
$cardinfo[] = $data;
}
fclose($handle);
}
else {
drupal_set_message(t('CSV file doesn\'t exist'), 'error');
}
如果你有兴趣在这里是CSV文件的内容:
First Name,Last Name,Display Name,Nickname,Primary Email,Secondary Email,Screen Name,Work Phone,Home Phone,Fax Number,Pager Number,Mobile Number,Home Address,Home Address 2,Home City,Home State,Home ZipCode,Home Country,Work Address,Work Address 2,Work City,Work State,Work ZipCode,Work Country,Job Title,Department,Organization,Web Page 1,Web Page 2,Birth Year,Birth Month,Birth Day,Custom 1,Custom 2,Custom 3,Custom 4,Notes,
Ben,Gunn,Ben Gunn,Benny,[email protected],[email protected],,+94 (10) 11111111,+94 (10) 22222222,+94 (10) 33333333,,+94 44444444444,12 Benny Lane,,Beijing,Beijing,100028,China,13 asdfsdfs,,sdfsf,sdfsdf,134323,China,Manager,Sales,Benny Inc,,,,,,,,,,,
乔,康,乔 康,小康,,,,,,,,,,,,,,,北京市朝阳区,,,,,,,,,,,,,,,,,,,
答
只是写了一个答案什么的评论想通了:
fgetcsv
是locale敏感,所以请确保将setlocale
设置为UTF-8语言环境。
据我所见,fgetcsv()应该支持多字节字符。是什么让你觉得它不?你确定问题不在其他地方吗? – 2012-01-27 10:52:58
@Pekka'fgetcsv()'以字节为单位检查分隔符,所以如果分隔符**字节**可以是多字节**序列的一部分**则事情开始中断。 – 2012-01-27 10:56:52
@Eugen啊,你说得对。但是,应该不会发生与单字节字节匹配的多字节字节,至少在UTF-8中不会发生这种情况吗?在UTF-8中唯一的一个不是否定的是一个多字节分隔符(**编辑:** ahh,我猜它可能会在第二个字节中发生,你说得对。 ) – 2012-01-27 10:58:22