位(bit)、位元組(byte)、字元、編碼之間的關係
1、位:
資料儲存的最小單位。每個二進位制數字0或者1就是1個位;
2、位元組:
8個位構成一個位元組;即:1 byte (位元組)= 8 bit(位);
1 KB = 1024 B(位元組);
1 MB = 1024 KB; (2^10 B)
1 GB = 1024 MB; (2^20 B)
1 TB = 1024 GB; (2^30 B)
3、字元:
a、A、中、+、*、の......均表示一個字元;
一般 utf-8 編碼下,一個漢字 字元 佔用 3 個 位元組;
一般 gbk 編碼下,一個漢字 字元 佔用 2 個 位元組;
4、字符集:
即各種各個字元的集合,也就是說哪些漢字,字母(A、b、c)和符號(空格、引號..)會被收入標準中;
5、編碼:
規定每個“字元”分別用一個位元組還是多個位元組儲存,用哪些位元組來儲存,這個規定就叫做“編碼”。(其實際是對字符集中字元進行編碼,即:每個字元用二進位制在計算中表示儲存);
通俗的說:編碼就是按照規則對字元進行翻譯成對應的二進位制數,在計算器中執行儲存,使用者看的時候(比如瀏覽器),在用對應的編碼解析出來使用者能看懂的;
(1)標準ASCii字符集:有96個列印字元,和32個控制字元組成;一共96+32=128個;
用7位二進位制數來對每1個字元進行編碼;
而由於7位還還不夠1個位元組,而電腦的內部常用位元組來用處理,每個位元組中多出來的最高位用0替代;
0 000 0000.........................0
0 111 1111..........................127; 從0----127,來表示128個ACSii編碼;
比如:字元 'A'----------在計算器內部用0100 0001 (65)來表示;
字元'a'-----------在計算器內部用0 110 0001 (97)來表示;
注意:'10'在計算器內部是沒有編碼的,因為它是字串,而不是單個字元。可以分別對1,0字元編碼儲存;
(2)擴充套件ASCii字符集:將標準的ASCii最高位1,得到十進位制程式碼128---255(1 000 0000----1 111 1111);所以字符集一共有0---255, 256個字元;
(3)gb2312字符集: 所有漢字字元在計算機內部採用2個位元組來表示,每個位元組的最高位規定為1【正好與標準ASCii字元(最高位是0)不重疊,併兼容】,不支援繁體字;
所以:gb2312表示漢字的編碼為:[129--255][129--255] (兩個位元組,每個位元組最高位是1);小於127的字元,與ASCii編碼相同;
(4)gbk字符集:gb2312的擴充,相容gb2312,除了收錄gb2312所有的字元外,還收錄了其他不常見的漢字、繁體字等;
gbk中字元是一個或兩個位元組,單位元組字元00--7F(0---127)這個區間和ASCII是一樣的;
雙位元組字元的第一個位元組是在81--FE(129--254)之間。通過這個可以判斷是單位元組還是雙位元組;
即:在gbk字元編碼,如果第一個位元組是>128的,則再往後找一個位元組,組成漢字;如果第一個位元組<128,則表示的是一個單位元組(此時和ASCII是一樣的);
<?php
// gbk編碼下,無亂碼擷取中文字元;
// 方法1:自定義函式1;
function subgbk($str,$lens){ // 形參 $str 表示將要擷取的原始字串, $lens 表示 設定需要擷取的 字元 個數;
if($lens<=0){
return '';
}
$chars = 0; // 計算,統計已經擷取的字元個數;
$res = ''; // 已經擷取的字元長度;
$offset = 0; // 偏移量,從字串中那個位元組開始擷取;
$lengths = strlen($str); // 將要擷取的原始字串 位元組 的數;
while($chars<$lens && $offset<$lengths){
$hight = ord(substr($str,$offset,1)); // 計算出 gbk字元中 高位元組的所對應編碼的值;
if($hight>128){
// 擷取兩個位元組,代表一個字元;即:雙位元組字元;
$count = 2;
}else{
//擷取一個位元組,代表一個字元,即:單位元組字元;
$count = 1;
}
$res .= substr($str,$offset,$count);
$offset += $count;
$chars++;
}
return $res;
}
?>
<?php
// 方法2:自定義函式2;
// 思路:先把字串中 所有的 字元 逐一取出來 組成陣列,然後對 陣列元素 進行擷取;最後截取出的陣列拼接成想要的字串;
function strgbk2($str,$strat,$length=NULL){ // 第三個引數預設設定的值是NULL,這個引數是參考了下邊的array_slice()函式,如果省略的話,則表示,一直擷取到最後末尾;
$zijielen = strlen($str); // 計算出 原始字串 $str 的 位元組長度;
$chars = 0; // 統計計算擷取的 字元 數;
$zifuarr = array(); //將字串按照 字元 的形式 分割到 陣列中;(待儲存);
for($i = 0;$i<$zijielen;){ // $i 表示 原始字串 $str 中的位元組(標記);
if(ord(substr($str,$i,1)) > 128){
// 雙位元組字元,兩個位元組來表示一個字元;
$zifuarr[] = $str[$i].$str[$i+1];
// $zifuarr[] = substr($str,$i,2); 兩種寫法的意思是一樣的,都是 取得 某個字元 某些的 位元組;
$i += 2;
}else{
// 單位元組字元,一個位元組表示一個字元;
$zifuarr[] = $str[$i];
// $zifuarr[] = substr($str,$i,1); 兩種寫法的意思是一樣的,都是 取得 某個字元 某個的 位元組;
$i++;
}
$chars++; // 每次迴圈,相當於擷取一個儲存 字元; 相當於所有的陣列元素個數 count($zifuarr);
}
if($chars < $strat){ // 當偏移 過 字元 總長度時候;(開始擷取的位置,已經超過了字元的總長度);
return 'No characters have been found !';
}
return implode(array_slice($zifuarr,$strat,$length)); // array_slice()是擷取陣列一部分;implode()是將截取出來的字串,連結起來;
}
?>
<?php
// 方法3:用php內建的函式 mb_substr擷取;
//此函式和 mb_strlen()函式一樣,都是針對 多位元組 字元 的操作函式,在特定的編碼下,針對 字元 的 擷取(mb_substr)和長度的計算(mb_strlen);
$strgbk = 'ds我是fdg一箇中國xghdt人';
echo mb_substr($strgbk,2,3,'gbk'); // 我是f
?>
(5)Unicode字符集:容納世界上所有語言字元和符號的集合;(以及對應的二進位制數字);
Unicode只是一個編碼規範,目前實際實現的unicode編碼只要有三種:UTF-8,UCS-2和UTF-16,三種unicode字符集之間可以按照規範進行轉換。
(6)utf-8編碼:UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼,也是一種字首碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII兼容,這使得原來處理ASCII字元的軟體無須或只須做少部分修改,即可繼續使用。因此,它逐漸成為電子郵件、網頁及其他儲存或傳送文字的應用中,優先採用的編碼。
對於UTF-8編碼中的任意位元組B,如果B的第一位為0,則B獨立的表示一個字元(ASCII碼);
如果B的第一位為1,第二位為0,則B為一個多位元組字元中的一個位元組(非ASCII字元);
如果B的前兩位為1,第三位為0,則B為兩個位元組表示的字元中的第一個位元組;
如果B的前三位為1,第四位為0,則B為三個位元組表示的字元中的第一個位元組;
如果B的前四位為1,第五位為0,則B為四個位元組表示的字元中的第一個位元組;
因此,對UTF-8編碼中的任意位元組,根據第一位,可判斷是否為ASCII字元;根據前二位,可判斷該位元組是否為一個字元編碼的第一個位元組;根據前四位(如果前兩位均為1),可確定該位元組為字元編碼的第一個位元組,並且可判斷對應的字元由幾個位元組表示;根據前五位(如果前四位為1),可判斷編碼是否有錯誤或資料傳輸過程中是否有錯誤。
即:
1、單位元組的字元,位元組的第一位設為0,對於英語文字,UTF-8碼只佔用一個位元組,和ASCII碼完全相同;
2、n個位元組的字元(n>1),第一位元組的前n位設為1,第n+1位設為0,後面位元組的前兩位都設為10;
3、2個位元組,第一個位元組的前2位是1;3個位元組,第一個位元組的前三位是1; 4個位元組,第一個位元組的前4位都是1;
// 如何擷取中文,無亂碼,假設utf-8編碼;
<?php
// 方法1:自定義函式;
function subutf8($str,$len){ // 兩個形式引數,$str代表原始目標字串,$len表示需要擷取 字元 的個數; 注意是 字元的 個數;
if($len<=0){ // 當 將要擷取的字元數 <=0的時候 返回 空字串;
return '';
}
$chars = 0 ; // $chars 引數 代表 已經擷取的字元的個數, 預設初始設定為0;
$res = ''; // $res 引數 表示 已經擷取的字串的長度, 預設初始設定為空(字串);
$offset = 0; // $offset 引數 表示 位元組 擷取的偏移量; 即從字串中(偏移幾個位元組)哪個位元組開始擷取;
$lengths = strlen($str); // $lengths 引數 表示 原始整個字串的 位元組的長度; 而應保證 $offset < $lengths; 否則都無字元可擷取;
while($chars<$len && $offset < $lengths){ // $chars<$len; 應是< 而不是<= ;因為是從0開始的,並執行迴圈擷取的;
$higher = decbin(ord(substr($str,$offset,1))); // 計算出 一個字元 中 第一個位元組(高位元組)的二進位制;
if(strlen($higher) < 8){ // decbin()函式 轉換出來的 二進位制數 如果 <128(10000000) 的時候,即是二進位制長度<8位的時候,前面高位不預設補0;
// 擷取一個位元組;(表示一個字元);
$count = 1;
}else if(substr($higher,0,3) == '110'){
//擷取兩個位元組(表示一個字元);
$count = 2;
}else if(substr($higher,0,4) == '1110'){
//擷取三個位元組(表示一個字元);
$count = 3;
}else if(substr($higher,0,5) == '11110'){
//擷取四個位元組(表示一個字元);
$count = 4;
}else if(substr($higher,0,6) == '111110'){
//擷取五個位元組(表示一個字元);
$count = 5;
}else if(substr($higher,0,7) == '1111110'){
//擷取六個位元組(表示一個字元);
$count = 6;
}
$res .= substr($str,$offset,$count);
$offset += $count;
$chars++;
}
return $res;
}
?>
<?php
//方法2:使用系統函式擷取
mb_substr(),不會亂碼.例:
$str = '換s幾ss個字實現中文字串擷取無亂碼的方法。';
echo mb_substr($str,0,5,'UTF-8');// '換s幾ss'
?>php
// 中文字串實現反轉;
$a = '我是一個好人';
function strrevv($str) {
$strlen = mb_strlen($str,'UTF-8');
$arr = array();
for($a=0;$a<=$strlen;$a++) {
$arr[] = mb_substr($str,$a,1,'UTF-8');
}
$newstr = implode('',array_reverse($arr));
return $newstr;
}
echo strrevv($a);
// mb_strlen(str,utf-8); 字串中字元的個數;
// mb_substr(str,start,length,utf-8); 多位元組字元的擷取;
// mb_strpos(str,needle,offset,utf-8);查詢子字串,在字串中出現的位置;
// mb_substr_count(str,needle,utf-8);統計子字串,在字串中出現的次數;