前阵子搞了个导出CSV文件的需求, 带出字符中包含了日语和韩语,如果直接
$data=iconv("utf-8", "gb2312", $data);
在Mac下韩文是乱码的, 如果我不转换编码,直接导出内容,在Mac下是正常的,但是到Windows下导出,就是乱码的。
一般csv都是由excel打开的,因为excel沿用了windows判断编码的逻辑,当发现无BOM时,采用gb18030编码进行解码。要解决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:
ASCII
这套编码规则是由美国定制,一共规定了128个字符的编码,比如空格"SPACE"是32(十进制)(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括 32个不能打印出来的控制符号),只占用了一个字节(8 bit)的后面7位,最前面的1位统一规定为0。总共才有128个字符编码,一个字节都没有用完,这好像似乎有点太少了。于是乎,就开始压榨最高位,对其为1时也进行编码,利用最高位进行编码的方式就称为非ASCII编码,如ISO-8859-1编码。ASCII码表_全_完整版
Unicode
一个符号集,里面包含了可能出现的所有字符,每个字符对应一个数字,这个数字称为码点(Code Point),如字符'H'的码点为72(十进制),字符'李'的码点为26446(十进制)。Unicode表包含了1114112个码点,即从000000(十六进制) - 10FFFF(十六进制)。地球上所有字符都可以在Unicode表中找到对应的唯一码点。点击这里,查询字符对应的码点。Unicode将码空间划分为17个平面,从00 - 10(十六进制,最高两位),即从0 - 16(十进制),每个平面有65536个码点(2^16)。
Unicode符号范围 | UTF-8编码方式 |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
细节就是魔鬼
计算机的历史只有短短不到100年的时间,而互联网则只有不到30年。因为历史很短,很多时候我们会产生一种假象,那就是计算机的历史好像是笔直的,一切设计都很合理、恰到好处,只需理解一下高抽象层次的概念和原理即可。而事实上则恰好相反,计算机世界的历史崎岖不平,充满了错误和因为错误而颠簸的设计,这里面隐藏了大量的细节。有时我们假装自己已经对程序了如指掌,“啊,编码嘛,不就是映射一下嘛;哦,HTTP协议嘛,很简单啊,就是个抽象层而已啊”,假装自己是高级程序员,因此好像可以忽略这些细节。实际呢,到处都是坑!
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://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
发表评论
欢迎评论
OydBmhbFgJC
VxdzHvuaiUKStnlq
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
<%- 965561485 992830243 %>
123456
123456
123456
#set($c=984205109 875324135)${c}$c
123456
${(956011232 875154467)?c}
123456
123456
123456
${885582684 808411931}
123456
123456
/*1*/{{946899950 814592708}}
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
'-var_dump(md5(655141707))-'
123456
123456
123456
123456
${@var_dump(md5(445772210))};
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
${951432369 965154040}
123456
123456
123456
123456
123456
%{44574*43011}
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
123456
' (44570*40783) '
123456
123456
123456
123456
123456
123456
123456
123456
123456