RJ博客

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

本文目录

前阵子搞了个导出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,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 007F0xxxxxxx
0000 0080-0000 07FF110xxxxx 10xxxxxx
0000 0800-0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF11110xxx 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://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

相关推荐

发表评论