RJ博客

从CSV文件乱码解决到字符编码详解

本文目录

前阵子搞了个导出CSV文件的需求, 带出字符中包含了日语和韩语

如果直接

$data=iconv("utf-8", "gb2312", $data);

在Mac下韩文是乱码的, 如果我不转换编码直接导出内容在Mac下是正常的但是到Windows下导出就是乱码的。要解决PHP生成CSV文件的乱码问题,只需要在文件的开始输出BOM头,告诉系统CSV文件的编码方式,从而让Excel打开CSV时采用正确的编码。下面列举两种方法:

1) 把编码转换成UTF-16LE编码。

使用制表符(\t)而不是逗号作为分隔符(不用担心,它仍然是技术上CSV)。 编码为UTF-8之后,整个CSV字符串转换为UTF-16LE:

mb_convert_encoding($csv_content, 'UTF-16LE', 'UTF-8');

前缀的CSV字符串以little-endian字节顺序标记(LE BOM) :

$csv_content = chr(255) . chr(254) . $csv_content;
/*
 * OR $csv_content = "\xFF\xFE". $csv_content;
 */

好奇类似 \xFF 代表什么, 去了解了下 :

0xEF表示一个十六进制数,等于十进制数255。

至于这个0xEF,在程序中表示什么,那要看具体的使用场合而定。

若程序将它看作是一个八位的无符号整数,那就是十进制数255;

若程序将它看作是一个八位的有符号整数,那就是十进制数-1;

若程序将它看作是一个布尔量,那就是逻辑值“真”;

若程序将它看作是一个扩展ASCII字符,那就是'ï'(一个汉字在计算机中的存储,通常由二个或更多个扩展ASCII字符或标准ASCII字符组成,依它所采用的编码规则而定,如GB2312-80、BIG5,Unicode,UTF-8等等);

再或者,它是一个大整数的一部分、一个浮点数的一部分、它是一个计算机指令的一部分。


2) 采用UTF8编码并告诉系统CSV文件的编码。

 header('Expires: 0');
 header('Cache-control: private');
 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 header('Content-Description: File Transfer');
 header('Content-Encoding: UTF-8');
 header('Content-type: text/csv; charset=UTF-8');
 header('Content-Disposition: attachment; filename=Customers_Export.csv');

 echo "\xEF\xBB\xBF"; // UTF-8 BOM
 /*
 * OR print( chr(0xEF).chr(0xBB).chr(0xBF) );
 * OR print( chr(239).chr(187).chr(191) );
 */

至此乱码问题算是解决了,下面继续了解什么是BOM?

    在UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

    Windows就是使用BOM来标记文本文件的编码方式的。列举几种BOM头:

define ('UTF32_BIG_ENDIAN_BOM', chr(0x00) . chr(0x00) . chr(0xFE) . chr(0xFF));
define ('UTF32_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE) . chr(0x00) . chr(0x00));
define ('UTF16_BIG_ENDIAN_BOM', chr(0xFE) . chr(0xFF));
define ('UTF16_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE));
define ('UTF8_BOM', chr(0xEF) . chr(0xBB) . chr(0xBF));

去除UTF-8 BOM头方法

1. chr(239) . chr(187) . chr(191)拼接成utf-8 bom头,再使用trim函数即可

trim($param, chr(239) . chr(187) . chr(191))

2.匹配是否存在bom头,存在则截掉前三位字符,即bom头

if(preg_match('/^\xEF\xBB\xBF/',$json_data)){
    $json_data = substr($info,3);
}
$json_data= json_decode(trim($json_data),true);


最后了解下几种字符编码的区别:ASCII,Unicode 和 UTF-8,参考阮一峰的博客:

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

详细的字符编码详解

https://juejin.im/post/5ca3389751882543dd788f62

https://blog.51cto.com/polaris/377468


顺便补一下原码补码反码知识:

https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

https://blog.csdn.net/qq_22771739/article/details/84496115


Refer:

https://stackoverrun.com/cn/q/7744776

https://www.cnblogs.com/wwjchina/p/9603286.html BOM头及去除方法

https://www.itranslater.com/qa/details/2124746542811710464

https://blog.csdn.net/chaozhi_guo/article/details/51003590

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html 阮一峰的博客

https://stackoverflow.com/questions/4348802/how-can-i-output-a-utf-8-csv-in-php-that-excel-will-read-properly/4762708#4762708

https://www.php.net/manual/zh/function.mb-detect-encoding.php

https://juejin.im/post/5ca3389751882543dd788f62 详细的字符编码详解

https://blog.51cto.com/polaris/377468  彻底理解掌握编码知识

http://www.regexlab.com/zh/encoding.htm

https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

https://blog.csdn.net/qq_22771739/article/details/84496115

相关推荐

发表评论