###乱码原因 > 文件保存的时候是按照ANSI编码(其实就是GB2312)保存,打开的时候程序按照UTF-8方式对内容解释,于是就出现了乱码。避免乱码的方式很简单,在“文件”菜单中选择“打开”命令,选择保存的文件,然后选择“ANSI”编码,此时就能看到久违的“联通”两个字了。 ###ASCII > 计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122 > ASCII 码使用7位二进制数来表示128个字符,也就是用一个字节来表示,最前的一位默认为 0。linux命令查看man ascii ![Alt text](http://segmentfault.com/img/bVcdqg) > 如果把最后一位也用起来的话,也就是8位二进制,那么就可以表示256个字符了,于是扩展 ASCII 码诞生,保留原始的7位的基础上,使用了最前的一位。 ![Alt text](http://segmentfault.com/img/bVcdqm) ###中文编码 GB2312 GBK GB18030 > 国内的GB2312(信息交换用汉字编码字符集),使用两个字节,也就是16位二进制,那么算下来最多就可以存下 65536 个字符了,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。 ###天下归一Unicode ###Unicode虽然能容纳上百万数量的字符,但是它只是一个巨大的字符集,仅仅规定了每个符号的二进制代码,却没有制定细化的存储规则. > 例如当用三个字节存储一个字符时,它同时也可以被理解为存储了三个 ASCII 码,另外我们之前知道 ASCII 码只需要一个字节,但是如果 Unicode 规定每个字符使用三个字节来存储的话,那岂不是额外浪费两个字节的空间? > ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。 > 字母A用ASCII编码是十进制的65,二进制的01000001; > 字符0用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的; > 汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。 > 字符 ASCII Unicode UTF-8 > A 01000001 00000000 01000001 01000001 > 中 x 01001110 00101101 11100100 10111000 10101101 > 在Unicode中:汉字“字”对应的数字是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32 ###从Unicode到UTF-8的编码方式如下: > Unicode编码(16进制) UTF-8 字节流(二进制) > 000000 – 00007F 0xxxxxxx > 000080 – 0007FF 110xxxxx 10xxxxxx > 000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx > 010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx > “汉”字的Unicode编码是0x6C49。0x6C49在0×0800-0xFFFF之间,使用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。 > 「字符集」和「编码」两个概念应该区分开,字符集定义了一组字符,编码定义了如何来表示字符集中的字符。比如 Unicode 是一个「字符集」,而 UTF-8 是一种「编码方式」,其他的编码方式还有 UTF-16 和 UTF-32 ###BOM(byte-order mark) > Unicode编码中表示字节排列顺序的那个文件头,叫做BOM(byte-order mark),FFFE和FEFF就是不同的BOM > UTF编码 Byte Order Mark > UTF-8 EF BB BF > UTF-16LE FF FE > UTF-16BE FE FF > UTF-32LE FF FE 00 00 > UTF-32BE 00 00 FE FF ![Alt text](http://segmentfault.com/img/bVcdqu) > UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字节顺序是不变的,因此这个文件头实际上不起作用。有一些编程语言是ISO-8859-1编码,所以如果用UTF-8针对这些语言编程序,就必须去掉BOM,即保存成“UTF-8—无BOM”的格式才可以,PHP语言就是这样。 > 在UTF-8文件中放置BOM主要是微软的习惯,BOM其实是为UTF-16和UTF-32准备的,微软在UTF-8使用BOM是因为这样可以把UTF-8和ASCII等编码明确区分开 ###编码对照 > 汉字 Unicode(ucs-2)10进制表示 Utf-8 Utf-16 Utf32 区位码 GB2312/GBK/GB18030 > 中 20013 E4 B8 AD 4E2D 00004E2D 5448 D6D0 > 文 25991 E6 96 87 6587 00006587 4636 CEC4 ###php与编码 //php判断文件编码 ```php 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)); $_req = trim($_GET['url']); header("Content-Type: text/plain;charset=utf-8"); if( 'UTF-16LE' === detect_utf_encoding( $_req)){ echo iconv('UTF-16', 'UTF-8', file_get_contents( $_req)); }else { echo file_get_contents( $_req); } function detect_utf_encoding($filename) { $text = file_get_contents($filename); $first2 = substr($text, 0, 2); $first3 = substr($text, 0, 3); $first4 = substr($text, 0, 3); if ($first3 == UTF8_BOM) return 'UTF-8'; elseif ($first4 == UTF32_BIG_ENDIAN_BOM) return 'UTF-32BE'; elseif ($first4 == UTF32_LITTLE_ENDIAN_BOM) return 'UTF-32LE'; elseif ($first2 == UTF16_BIG_ENDIAN_BOM) return 'UTF-16BE'; elseif ($first2 == UTF16_LITTLE_ENDIAN_BOM) return 'UTF-16LE'; } //字符分割 $str = "Hello Friend统计" ; print_r(explode(' ',$str)); $arr1 = str_split ( $str );// 中文乱码 $arr2 = str_split ( $str , 3 );按3个字节分割 print_r($arr1); print_r($arr2); Array ( [0] => Hello [1] => Friend统计 ) Array ( [0] => H [1] => e [2] => l [3] => l [4] => o [5] => [6] => F [7] => r [8] => i [9] => e [10] => n [11] => d [12] => � [13] => � [14] => � [15] => � [16] => � [17] => � ) Array ( [0] => Hel [1] => lo [2] => Fri [3] => end [4] => 统 [5] => 计 ) function str_split_unicode($str, $l = 0) { if ($l > 0) { $ret = array(); $len = mb_strlen($str, "UTF-8"); for ($i = 0; $i < $len; $i += $l) { $ret[] = mb_substr($str, $i, $l, "UTF-8"); } return $ret; } return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY); } $s = '无符号整数'; print_r(str_split_unicode($s)); print_r(str_split_unicode($s, 3)); Array ( [0] => 无 [1] => 符 [2] => 号 [3] => 整 [4] => 数 ) Array ( [0] => 无符号 [1] => 整数 ) //php json_encode class myClass { public $item1 = 1; public $item2 = '中文'; function to_json() { //url编码,避免json_encode将中文转为unicode $this->item2 = urlencode($this->item2); $str_json = json_encode($this); //url解码,转完json后将各属性返回,确保对象属性不变 $this->item2 = urldecode($this->item2); return urldecode($str_json); } } $c = new myClass(); echo json_encode($c);//{"item1":1,"item2":"\u4e2d\u6587"} echo $c->to_json();//{"item1":1,"item2":"中文"} //php 转换实体 $s = '&#36716;&#25442;&#23454;&#20307;' $s = mb_convert_encoding($s, 'UTF-8', 'HTML-ENTITIES');// or html_entity_decode // 前三个都是Unicode原始值,最后一个是UTF-8 编码1:&#27979;&#35797; 编码2:\u6d4b\u8bd5 编码3:&#x6D4B;&#x8BD5; 编码4:%e6%b5%8b%e8%af%95 ``` ###php BOM > PHP不认识文件中的BOM头并会将其作为HTTP Response的正文送出。这甚至在无缓冲的情况下,会导致header()等必须在Response开始前执行的函数直接失效 ```php function remove_utf8_bom($text) { $bom = pack('H*','EFBBBF'); $text = preg_replace("/^$bom/", '', $text); return $text; } //eg : $header = array( "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" ); $curl = curl_init('http://www.btc38.com/trade/getTradeList.php?coinname=XRP'); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, $header); $res = curl_exec($curl); curl_close($curl); $res = substr($res, 3); $data=json_decode($res,true); var_dump($data); ``` ###php urldecode ```php $request= "abc%2527 or 1=%2527";//模拟注入 $request= urldecode("abc%2527 or 1=%2527"); echo "<a href='?name={$request}'>click me</a>"; echo $_GET['name'];//单引号被 urlencode 两次以后是 %2527,字符串%2527提交到后台,php首先进行一次自动解码,%25编码后是%,因此我们收到的字符串是%27,而如果这时我们调用了urldecode的话,会再次进行解码,%27变为' ###php 检测字符串是否为UTF8编码 /** * 检测字符串是否为UTF8编码 * @param string $str 被检测的字符串 * @return boolean */ function is_utf8($str){ $len = strlen($str); for($i = 0; $i < $len; $i++){ $c = ord($str[$i]); if ($c > 128) { if (($c > 247)) return false; elseif ($c > 239) $bytes = 4; elseif ($c > 223) $bytes = 3; elseif ($c > 191) $bytes = 2; else return false; if (($i + $bytes) > $len) return false; while ($bytes > 1) { $i++; $b = ord($str[$i]); if ($b < 128 || $b > 191) return false; $bytes--; } } } return true; } ``` ###javascript与编码 > JavaScript中的String内部表示方式始终是UTF16,而它的length也是始终按UTF16 code point去计算,简而言之,length始终返回字符数量,而非字节大小! ```javascript console.log('javascript'.length);// 10 console.log('中文'.length);// 2 console.log('中文'.replace(/[\u4e00-\u9fa5]/g,'aa').length)//把中文当2个字符 //html实体转换 var decodeHtmlEntity = function(str) { return str.replace(/&#(\d+);/g, function(match, dec) { return String.fromCharCode(dec); }); }; var str = 'JavaScript&#39640;&#32423;&#31243;&#24207;&#35774;&#35745;'; console.log(decodeHtmlEntity(str));//JavaScript高级程序设计 //以下代码用于将正常的字符转变为html实体 var encodeHtmlEntity = function(str) { var buf = []; for (var i=str.length-1;i>=0;i--) { buf.unshift(['&#', str[i].charCodeAt(), ';'].join('')); } return buf.join(''); }; var str = '高级程序设计'; console.log(encodeHtmlEntity(str));//&#39640;&#32423;&#31243;&#24207;&#35774;&#35745; //单字符转换 function convert2Unicode(char) { return "\\u" + char.charCodeAt(0).toString(16); } function convert2Unicode2(s){ s = escape(s) return s.replace(/%u/g,'\\u'); } //多字符转换 function toUnicode(theString) { var unicodeString = ''; for (var i = 0; i < theString.length; i++) { var theUnicode = theString.charCodeAt(i).toString(16).toUpperCase(); while (theUnicode.length < 4) { theUnicode = '0' + theUnicode; } theUnicode = '\\u' + theUnicode; unicodeString += theUnicode; } return unicodeString; } toUnicode('字符编码那些事')//"\u5B57\u7B26\u7F16\u7801\u90A3\u4E9B\u4E8B" unescape("\u5B57\u7B26\u7F16\u7801\u90A3\u4E9B\u4E8B");//字符编码那些事 //十进制转其他 var x=110; x; x..toString(8); x..toString(32); x..toString(16); //其他转十进制 var x='110'; parseInt(x,2); parseInt(x,8); parseInt(x,16); //php base_convert($binary,2,16) ``` ###javascript 编码函数 ```javascript unescape('\u5b57\u7b26\u7f16\u7801\u90a3\u4e9b\u4e8b')//字符编码那些事 node根本不需要 unescape 调用: node > a='\u5b57\u7b26\u7f16\u7801\u90a3\u4e9b\u4e8b' ``` > 无论网页的原始编码是什么,一旦被Javascript编码,就都变为unicode字符。也就是说,Javascipt函数的输入和输出,默认都是Unicode字符 ###奇怪字符 new Array(20).join('\u0310') f́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́́ ###python与编码 > python当中默认的编码格式是Unicode,然后如果你想要将一种字符转换成另一种字符是不能直接转换的,而是先应该使用decode函数将特殊编码转换成Unicode,然后将Unicode再转换成另一种字符,比如将GBK转换成utf8 str.decode('gbk').encode('utf-8') ```python u'中文'.encode('utf-8') '\xe4\xb8\xad\xe6\x96\x87' print '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') #中文 ``` ###解决乱码 > 需要告诉浏览器自己的文件采用了什么字符编码,下面列出一些常见的方法: ```php header('content-type:text/html;charset=utf-8;'); ``` ```html <meta charset="gb2312"> //html5 <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> //html4 xhtml <script src="http://ossweb-img.qq.com/images/js/foot.js" charset="gb2312"></script> <link href="http://gameweb-img.qq.com/css/common.css" rel="stylesheet" charset="gb2312" > ``` ![Alt text](http://djt.qq.com/upload/public/common/2013/09/images/02142304314.jpg?nothumb=false) ###感谢 [维基百科](http://zh.wikipedia.org/zh-cn/UTF-8#UTF-8.E7.9A.84.E7.B7.A8.E7.A2.BC.E6.96.B9.E5.BC.8F) [字符编码的前世今生](http://djt.qq.com/article/view/658?ADTAG=email.InnerAD.weekly.20130902&bsh_bid=281085951) [汉字unicode编码表](http://www.chi2ko.com/tool/CJK.htm) [字符编码笔记](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) http://www.w3.org/TR/REC-xml/#sec-physical-struct [中文编码杂谈](http://blog.sae.sina.com.cn/archives/2279) [奇怪字符](http://www.fileformat.info/info/unicode/category/Mn/list.htm) [UTF-8, UTF-16, UTF-32 & BOM](http://www.unicode.org/faq/utf_bom.html) [js中的UTF-8编码与解码](http://qiutianaimeili.com/html/page/2018/07/m0kgr2tw7u.html) 下载编码转换工具