PHP: chr和pack、unpack那些事

PHP是一門很靈活的語言。正由於它太靈活了,甚至有些怪異,因此你們對它的評價褒貶不一。其實我想說的是,任何一門語言都有它自身的哲學,有它存在的出發點。PHP爲Web而生,它以快速上手、快速開發而著稱,因此它也常被冠以簡單、新手用的語言等標籤。我倒不這麼認爲,所謂選對的工具去作對的事,沒有包打天下的語言。而至於說其簡單,卻也未必。javascript

引子

我以前有篇文詳細介紹過pack和unpack:PHP: 深刻pack/unpack ,若是有不明白的地方,建議再回過頭去看多幾遍。如今應該可以寫出如下代碼:php

<?php
echo pack("C", 97) . "\n";

$ php -f test.php
a

可是,爲何會輸出'a'呢?雖然咱們知道字符'a'的ASCII碼就是97,可是pack方法返回的是二進制字符串,爲何不是輸出一段二進制而是'a'?爲了確認pack方法返回的是一段二進制字符串,這裏我對官方的pack的描述截了個圖:
html

確實如此,pack返回包含二進制字符串的數據,接下來詳細進行分析。前端

程序是如何顯示字符的

這裏所說的'程序',實際上是個宏觀的概念。java

對於在控制檯中執行腳本(這裏是指PHP做爲cli腳原本執行),腳本的輸出會寫入標準輸出(stdin)或標準錯誤(stderr),固然也有可能會重定向到某個文件描述符。拿標準輸出來講,暫且忽略它是行緩衝、全緩衝或者是無緩衝。腳本進程執行完畢後若是有輸出則會在控制檯上輸出字符串。那這裏的控制檯就是所說的'程序'。
shell

對於Web來講(這裏是指PHP做爲Web的服務器端語言),程序執行完後會將結果響應給瀏覽器或其它UserAgent,爲了方便描述,這裏統一稱爲UserAgent。這裏的UserAgent就是所說的'程序'。數據庫

固然還有其它狀況,好比在GUI窗口中的輸出,編輯器打開一個文件等等,這都涉及到如何顯示字符串的問題。windows

在控制檯中執行

控制檯經過shell命令來執行腳本,它會fork一個子進程,以後經過exec替換子進程的地址空間,由於這個子進程不是會話首進程,因此它能夠關聯到控制終端。腳本輸出執行完畢後退出,回到控制檯。來看下面的例子:瀏覽器

<?php
$str = '回';
echo $str . "\n";

$ php -f test.php
回

test.php是UTF-8編碼的文件,個人Linux系統的Locales是zh_CN.UTF-8。服務器

$ locale
LANG=zh_CN.UTF-8
LANGUAGE=
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

回到剛纔的代碼,test.php是UTF-8編碼的文件,漢字'回'是三個字節表示的UTF8字符(若是不明白,能夠看個人另外一篇文章:JavaScript: 詳解Base64編碼和解碼),因此test.php文件的內容保存在硬盤上的數據就是4個字節('\n'是ASCII字符,用1個字節表示)。test.php執行輸出時,將這4個字節發送到標準輸出,以後被沖洗(這裏忽略掉被flush的時機),由控制檯來顯示。回想一下Linux系統上的locale設置,很顯然是採用UTF8的機制來顯示字符,因此前三個字節被當成一個UTF8字符,它被組合在一塊兒轉成Unicode碼而後查表,再顯示出來。

<?php
$str = '回';
echo $str . "\n";
echo $str{0} . $str{1} . $str{2} . "\n";

$ php -f test.php
回
回

能夠看到,不論是整個字符輸出,仍是三個字節連在一塊兒輸出,結果是同樣的。咱們接下來看看不一樣平臺上同一個字符的Unicode編碼和UTF-8編碼是否同樣:

PHP測試:

<?php
$str = '回';
$bin = pack("C3", ord($str{0}), ord($str{1}), ord($str{2}));
$hex = strtoupper(bin2hex($bin));
echo "UTF-8編碼: " . $hex . "\n";
/**
* 1110xxxx 10xxxxxx 10xxxxxx
*/
$byte1 = ord($str{0});
$byte2 = ord($str{1});
$byte3 = ord($str{2});
$c1    = (($byte1 & 0x0F) << 4) | (($byte2 & 0x3F) >> 2);
$c2    = (($byte2 & 0x03) << 6) | ($byte3 & 0x3F);
$dec   = (($c1 & 0x00FF) << 8) | $c2;
echo "Unicode編碼: " . $dec . "\n";

$ php -f test.php
UTF-8編碼: E59B9E
Unicode編碼: 22238

JavaScript測試:

<script type="text/javascript">
/**
* UTF16和UTF8轉換對照表
* U+00000000 – U+0000007F 	0xxxxxxx
* U+00000080 – U+000007FF 	110xxxxx 10xxxxxx
* U+00000800 – U+0000FFFF 	1110xxxx 10xxxxxx 10xxxxxx
* U+00010000 – U+001FFFFF 	11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* U+00200000 – U+03FFFFFF 	111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* U+04000000 – U+7FFFFFFF 	1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
var code = ('回').charCodeAt(0);
// 1110xxxx
var byte1 = 0xE0 | ((code >> 12) & 0x0F);
// 10xxxxxx
var byte2 = 0x80 | ((code >> 6) & 0x3F);
// 10xxxxxx
var byte3 = 0x80 | (code & 0x3F);

console.group('Test chr: ');
console.log("UTF-8編碼:", byte1.toString(16).toUpperCase() + '' + byte2.toString(16).toUpperCase() + '' + byte3.toString(16).toUpperCase());
console.log("Unicode編碼: ", code);
console.groupEnd();
</script>

咱們看到輸出是同樣的。

做爲Web的服務器端語言執行

此次無非是由剛纔的控制檯執行變成了UserAgent,其實道理仍是同樣的。服務器端PHP腳本輸出會經過HTTP的響應返回給UserAgent,那麼UserAgent就要對它進行顯示。固然,這裏還有點例外。數據是經過網絡做爲字節流發送回UserAgent,一般UserAgent有幾種方式來判斷字節流是屬於什麼編碼(或許還涉及到壓縮,但這裏將不考慮這個因素)。

服務器端能夠經過響應頭部來告訴UserAgent應該用什麼編碼來處理這些數據,好比:

<?php
header("Content-Type: text/html; charset=utf8");

或者是HTML頁面中的<meta />標籤,好比:

<meta charset="utf-8" />

可是萬一這兩種方式都沒有提供,那也只能靠猜了。事實也確實如此,據我所知,Firefox就是這麼作的,而且將代碼開源了:universalchardet 。可是這種方式並不能百分之百正確檢測,因此偶爾會訪問到亂碼的頁面。

編輯器打開一個文件

在windows上用notepad新建文本文件另存爲時有幾種編碼選項:ANSI, Unicode, Unicode BigEndian, UTF-8。

在其它編輯器中選項更多,包括有BOM和無BOM的。BOM是文件頭的前幾個字節,經過BOM,處理它的程序就知道這個文件是採用什麼編碼,而且是什麼字節序。然而在PHP中,歷來都沒有將BOM考慮進去,因此PHP解釋器去執行一個PHP文件時,不會忽略前幾個BOM字節,這就致使了問題。通常的問題在於發送cookie前,BOM被輸出了。因此如今通常推薦無BOM的文件。

無BOM有時候也是會有問題的,由於這須要處理它的程序去檢測它是什麼編碼。檢測的方式通常是掃描文件,而後根據不一樣編碼的規則來判斷二進制。這裏舉一個出現問題的例子。在windows上新建一個文本文件並保存爲ANSI編碼,而後在文件中輸入'聯通',如圖所示:

保存好後關閉test.txt文件,而後再雙擊打開,如圖所示:

咱們看到顯示的是亂碼,具體咱們能夠分析一下產生亂碼的緣由。用Editplus新建一個ANSI文件,輸入'聯通',而後切換到十六進制查看方式,以下圖所示:

對應的十六進制是:C1 AA CD A8,轉成二進制後以下:

11000001 10101010 11001101 10101000

接着咱們來看下UTF-8的轉換表:

U+00000000 – U+0000007F 	0xxxxxxx
U+00000080 – U+000007FF 	110xxxxx 10xxxxxx
U+00000800 – U+0000FFFF 	1110xxxx 10xxxxxx 10xxxxxx
U+00010000 – U+001FFFFF 	11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U+00200000 – U+03FFFFFF 	111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U+04000000 – U+7FFFFFFF 	1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

很顯然都被看成了二字節的UTF-8字符,拿GBK的編碼去UTF-8的碼錶裏查,您說能查到嗎?

總結

如今咱們已經知道了不論是什麼編碼的數據,老是一個字節一個字的存儲,而且在存儲時會進行相應的編碼轉換。好比漢字'回'的GBK編碼和UTF-8編碼的字節數和編碼值都不同,因此在將GBK的文件另存爲UTF-8時必然會存在轉換,反之也是同樣的。而在讀取時若是有BOM就按BOM規定的編碼來處理,不然要進行編碼檢測後再處理。

再說pack

以前講了這麼多編碼方面的問題,其實就是爲了讓你們更好的理解接下來要講的。pack能夠將ASCII進行打包而後輸出(事實上就是將一個多字節變成多個單字節,以後能夠經過unpack轉換回來),這個咱們已經知道了。可是方式有不少種,原理是同樣的。咱們來詳細分析。對pack/unpack不太熟悉的仍是建議去翻看我以前的一篇文章:PHP: 深刻pack/unpack 。由於本人的機器是小端序的,因此本文只考慮小端序。大端序是同樣的方式,只不過字節序不同罷了,能夠先判斷本機的字節序再處理。

<?php
echo pack("C", 0x61) . "\n";
echo pack("S", 0x6161) . "\n";
echo pack("L", 0x61616161) . "\n";
echo pack("L", 0x9E9BE561) . "\n";
echo chr(0xE5) . chr(0x9B) . chr(0x9E) . "\n";
echo pack("H6", "E59B9E") . "\n";

$ php -f test.php
a
aa
aaaa
a回
回
回

咱們一句句的來分析,首先是:

echo pack("C", 0x61) . "\n";
echo pack("S", 0x6161) . "\n";
echo pack("L", 0x61616161) . "\n";

這三句代碼很簡單,C是無符號字節,S是2個無符號字節,L是4個無符號字節,因此輸出也沒什麼疑問。不管幾個字節,都是ASCII碼,0x61的二進制的高位爲0,因此能正確顯示。

echo pack("L", 0x9E9BE561) . "\n";

咱們或許還記得漢字'回'的UTF-8編碼爲:0xE59B9E,L是按主機字節序打包的,而個人機器是小端序,因此0x9E9BE561打包後就變爲:0x61E59B9E。0x61是字符'a'的ASCII碼,然後面的三個字節程序經過判斷0xE5就能知道這是一個三字節的UTF-8字符,所以這三個字節會轉成Unicode碼去查表,而後顯示。

echo chr(0xE5) . chr(0x9B) . chr(0x9E) . "\n";

chr是返回ASCII碼所代碼的字符,它其實不只僅是轉換單字節的字符,對於多字節一樣適用。它會根據剛纔所說的規則將三個UTF-8字節轉成Unicode碼而後去查表。

echo pack("H6", "E59B9E") . "\n";

對於H格式字符,它和h的區別就是前者是高四位在前,後者是低四位在前,但它們都是以半字節爲單位讀取的,而且以十六進制的方式。您應該看到我在用H進行打包時傳的是字符串"E59B9E",若是傳的是0xE59B9E就不對了,這樣的話先會轉成十進制15047582,而後在前面加上0x變成十六進制0x15047582。

所謂按半字節讀取實際上是這樣的,好比0x47,先轉成十進制71,而後變成十六進制的0x71。按半字節讀取必然會丟棄4位,而後要補0。讀取了0x7,對H來講,它是高位,那麼在低位補0變成0x70。對於h來講,它是低位,那麼在高位補0變成0x07。

再說unpack

unpack是pack的逆函數,固然unpack有本身的語法,但這不是重點,由於這些只是表象。

unpack其實只是將多個字節壓縮成一個字節。好比0x12和0x34這兩個字節若是要組成一個雙字節,則可使用unpack的S格式化字符來實現,代碼以下:

<?php
$data = unpack("S", pack("H*", "3412"));
print_r($data);
echo '0x' . dechex($data[1]) . "\n";

$ php -f test.php
Array
(
    [1] => 4660
)
0x1234

由於是小端序,因此要寫成"3421"。其實還能夠用位運算的方式來實現。這個時候就不須要考慮字節序了,由於字節序只是存儲時才須要考慮的問題,對於輸出來講,是按照咱們天然的方式:

<?php
print "0x" . dechex((0x12 << 8) | 0x34) . "\n";

$ php -f test.php
0x1234

再說chr

PHP官方文檔上所描述的chr方法的原型參數是一個int型,雖然形參名爲ascii,但不要被騙了。如圖所示:

chr確實是能夠接收一個int類型的參數,而不只僅是一個ASCII碼。還記得以前所作的測試嗎?經過chr方法將三個UTF-8的字節組合在一塊兒。很顯然UTF-8的每一個字節都大於127,由於最高位都是1。

不過提及來chr方法仍是比較傻的,好比有以下代碼:

<?php
echo chr(0xE5) . chr(0x9B) . chr(0x9E) . "\n";
echo chr(0xE59B9E) . "\n";

$ php -f test.php
回
?

chr方法徹底沒有考慮將0xE59B9E拆成三個字節來組合,因此最終是亂碼。

再說ord

ord接受一個string類型的參數,它用於返回參數的ASCII碼。以下圖所示:

雖然它只返回ASCII碼,但它的參數卻不限定。好比您能夠傳遞單字節或多字節。舉例以下:

<?php
echo ord("a") . "\n";
echo ord("回") . "\n";
echo 0xE5 . "\n";

$ php -f test.php
97
229
229

傳入漢字'回',它會自動截取第一個字節,而後返回它的十進制表示。

實現本身的pack/unpack

理解了原理,其實本身去實現也就是那麼回事。本文以格式化字符L爲例,L是無符號32位整型,它是按主機字節序來打包的。因此咱們要先判斷機器的字節序。

<?php
function IsBigEndian()
{
	$bin = pack("L", 0x12345678);
	$hex = bin2hex($bin);
	if (ord(pack("H2", $hex)) === 0x78)
	{
		return FALSE;
	}

	return TRUE;
}

if (IsBigEndian())
{
	echo "大端序";
}
else
{
	echo "小端序";
}

echo "\n";

$ php -f test.php
小端序

代碼很是簡單,由於PHP不能直接操做內存,因此藉助於pack來實現。L格式化字符表示主機字節序,若是機器是小端序,則0x12345678經過L打包後會變成4個字節而且字節序是:0x78, 0x56, 0x34, 0x12,若是是大端序則是:0x12, 0x34, 0x56, 0x78。而後經過H2格式化字符獲取2個高4位(即一個高位字節),若是是0x78那就是小端序,不然就是大端序。

接下來是my_pack方法的實現,僅僅實現了L格式化字符,代碼以下:

<?php
// 判斷字節序
function IsBigEndian()
{
	$bin = pack("L", 0x12345678);
	$hex = bin2hex($bin);
	if (ord(pack("H2", $hex)) === 0x78)
	{
		return FALSE;
	}

	return TRUE;
}

// 自定義打包方法
function my_pack($num)
{
	$bin     = "";
	$padding = 0;

	if ($num >= 0x00 && $num <= 0xFF)
	{
		// 補3個字節
		$padding = str_repeat(chr(0), 3);

		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($num);
		}
		else
		{
			// 小端序
			$bin = chr($num) . $padding;
		}
	}
	else if ($num > 0xFF && $num <= 0xFFFF)
	{
		// 補2個字節
		$padding = str_repeat(chr(0), 2);
		$byte3   = ($num >> 8) & 0xFF;
		$byte4   = $num & 0xFF;
		
		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . $padding;
		}
	}
	else if ($num > 0xFFFF && $num <= 0x7FFFFF)
	{
		// 補1個字節
		$padding = chr(0);
		$byte2   = ($num >> 16) & 0xFF;
		$byte3   = ($num >> 8) & 0xFF;
		$byte4   = $num & 0xFF;

		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($byte2) . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . chr($byte2) . $padding;
		}
	}
	else
	{
		$byte1 = ($num >> 24) & 0xFF;
		$byte2 = ($num >> 16) & 0xFF;
		$byte3 = ($num >> 8) & 0xFF;
		$byte4 = $num & 0xFF;

		if (IsBigEndian())
		{
			// 大端序
			$bin = chr($byte1) . chr($byte2) . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . chr($byte2) . chr($byte1);
		}
	}

	return $bin;
}

$bin = my_pack(0x12);
print_r(unpack("L", $bin));
$bin = pack("L", 0x12);
print_r(unpack("L", $bin));

$bin = my_pack(0x1234);
print_r(unpack("L", $bin));
$bin = pack("L", 0x1234);
print_r(unpack("L", $bin));

$bin = my_pack(0x123456);
print_r(unpack("L", $bin));
$bin = pack("L", 0x123456);
print_r(unpack("L", $bin));

$bin = my_pack(0x12345678);
print_r(unpack("L", $bin));
$bin = pack("L", 0x12345678);
print_r(unpack("L", $bin));

$ php -f test.php
Array
(
    [1] => 18
)
Array
(
    [1] => 18
)
Array
(
    [1] => 4660
)
Array
(
    [1] => 4660
)
Array
(
    [1] => 1193046
)
Array
(
    [1] => 1193046
)
Array
(
    [1] => 305419896
)
Array
(
    [1] => 305419896
)

測試中調用pack和my_pack的結果是同樣的。unpack的實現就是pack的逆操做,只需把pack的結果的每個字節取到它的ASCII碼(能夠經過ord方法來獲得),而後將4個字節根據高低位次序(這還要根據大小端)經過位運算變成一個4字節的整數,其它格式化字符也是相似如此實現。接下來僅僅實現格式化字符L的unpack版本,代碼以下:

<?php
// 判斷字節序
function IsBigEndian()
{
	$bin = pack("L", 0x12345678);
	$hex = bin2hex($bin);
	if (ord(pack("H2", $hex)) === 0x78)
	{
		return FALSE;
	}

	return TRUE;
}

// 自定義打包方法
function my_pack($num)
{
	$bin     = "";
	$padding = 0;

	if ($num >= 0x00 && $num <= 0xFF)
	{
		// 補3個字節
		$padding = str_repeat(chr(0), 3);

		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($num);
		}
		else
		{
			// 小端序
			$bin = chr($num) . $padding;
		}
	}
	else if ($num > 0xFF && $num <= 0xFFFF)
	{
		// 補2個字節
		$padding = str_repeat(chr(0), 2);
		$byte3   = ($num >> 8) & 0xFF;
		$byte4   = $num & 0xFF;
		
		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . $padding;
		}
	}
	else if ($num > 0xFFFF && $num <= 0x7FFFFF)
	{
		// 補1個字節
		$padding = chr(0);
		$byte2   = ($num >> 16) & 0xFF;
		$byte3   = ($num >> 8) & 0xFF;
		$byte4   = $num & 0xFF;

		if (IsBigEndian())
		{
			// 大端序
			$bin = $padding . chr($byte2) . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . chr($byte2) . $padding;
		}
	}
	else
	{
		$byte1 = ($num >> 24) & 0xFF;
		$byte2 = ($num >> 16) & 0xFF;
		$byte3 = ($num >> 8) & 0xFF;
		$byte4 = $num & 0xFF;

		if (IsBigEndian())
		{
			// 大端序
			$bin = chr($byte1) . chr($byte2) . chr($byte3) . chr($byte4);
		}
		else
		{
			// 小端序
			$bin = chr($byte4) . chr($byte3) . chr($byte2) . chr($byte1);
		}
	}

	return $bin;
}

// 自定義解包方法
function my_unpack($bin)
{
	$byte1 = ord($bin{0});
	$byte2 = ord($bin{1});
	$byte3 = ord($bin{2});
	$byte4 = ord($bin{3});

	if (IsBigEndian())
	{
		// 大端序
		$num = ($byte1  << 24) | ($byte2 << 16) | ($byte3 << 8) | $byte4;
	}
	else
	{
		// 小端序
		$num = ($byte4  << 24) | ($byte3 << 16) | ($byte2 << 8) | $byte1;
	}

	return array($num);
}

$bin = my_pack(0x12);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));
$bin = pack("L", 0x12);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));

$bin = my_pack(0x1234);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));
$bin = pack("L", 0x1234);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));

$bin = my_pack(0x123456);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));
$bin = pack("L", 0x123456);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));

$bin = my_pack(0x12345678);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));
$bin = pack("L", 0x12345678);
print_r(unpack("L", $bin));
print_r(my_unpack($bin));

$ php -f test.php
Array
(
    [1] => 18
)
Array
(
    [0] => 18
)
Array
(
    [1] => 18
)
Array
(
    [0] => 18
)
Array
(
    [1] => 4660
)
Array
(
    [0] => 4660
)
Array
(
    [1] => 4660
)
Array
(
    [0] => 4660
)
Array
(
    [1] => 1193046
)
Array
(
    [0] => 1193046
)
Array
(
    [1] => 1193046
)
Array
(
    [0] => 1193046
)
Array
(
    [1] => 305419896
)
Array
(
    [0] => 305419896
)
Array
(
    [1] => 305419896
)
Array
(
    [0] => 305419896
)

知道了原理,實現起來就一點也不難,無非就是要注意字節序的問題。

關於ISO 8859-1編碼

ISO 8859-1又稱 Latin-1 或西歐語言。是國際標準化組織內ISO/IEC 8859的第一個8位字符集。它以ASCII爲基礎,在空置的0xA0-0xFF的範圍內,加入96個字母及符號,藉以供使用附加符號的拉丁字母語言使用。

從定義可知,Latin-1編碼是單字節編碼,向下兼容 ASCII ,其編碼範圍是0x00~0xFF。0x00~0x7F之間徹底和ASCII碼一致,0x80~0x9F之間是控制字符,0xA0~0xFF之間是文字符號。

ISO-8859-1收錄的字符除ASCII收錄的字符外,還包括西歐語言、希臘語、泰語、阿拉伯語、希伯來語對應的文字符號。歐元符號出現的比較晚,沒有被收錄在ISO-8859-1當中。

由於ISO-8859-1編碼範圍使用了單字節內的全部空間,在支持ISO-8859-1的系統中傳輸和存儲其餘任何編碼的字節流都不會被拋棄。換言之,把其餘任何編碼的字節流看成ISO-8859-1編碼看待都沒有問題。這是個很重要的特性,MySQL數據庫默認編碼是Latin-1就是利用了這個特性。ASCII編碼是一個7位的容器,ISO-8859-1編碼是一個8位的容器。

結束語

pack/unpack在實際工做中用得很是多,由於不少公司用PHP作前端,經過TCP調用接口,這就須要用到pack/unpack來打包和解包。但願本文能對你們有幫助。

相關文章
相關標籤/搜索