###乱码原因
> 文件保存的时候是按照ANSI编码(其实就是GB2312)保存,打开的时候程序按照UTF-8方式对内容解释,于是就出现了乱码。避免乱码的方式很简单,在“文件”菜单中选择“打开”命令,选择保存的文件,然后选择“ANSI”编码,此时就能看到久违的“联通”两个字了。
###ASCII
> 计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122
> ASCII 码使用7位二进制数来表示128个字符,也就是用一个字节来表示,最前的一位默认为 0。linux命令查看man ascii

> 如果把最后一位也用起来的话,也就是8位二进制,那么就可以表示256个字符了,于是扩展 ASCII 码诞生,保留原始的7位的基础上,使用了最前的一位。

###中文编码 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

> 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 = '转换实体'
$s = mb_convert_encoding($s, 'UTF-8', 'HTML-ENTITIES');// or html_entity_decode
// 前三个都是Unicode原始值,最后一个是UTF-8
编码1:测试
编码2:\u6d4b\u8bd5
编码3:测试
编码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 "click me";
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高级程序设计';
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));//高级程序设计
//单字符转换
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
//html5
//html4 xhtml
```

###感谢
[维基百科](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)
下载编码转换工具