最近看了不少編碼方面的文章煞抬,所以分二篇博文說(shuō)下“PHP疟羹、字符串县好、編碼围橡、UTF-8”相關(guān)知識(shí),本篇博文是上半部分缕贡,分為四大塊內(nèi)容翁授,分別是“字符串的定義和使用”、“字符串轉(zhuǎn)換”晾咪、“PHP 字符串的本質(zhì)”收擦、“多字節(jié)字符串”。上半部分比較基礎(chǔ)禀酱,下一篇文章《PHP 與 UTF-8的最佳實(shí)踐》可能干貨更多一點(diǎn)炬守。
字符串的定義和使用
PHP 中能夠通過(guò)四種方法設(shè)置字符串:
單引號(hào)字符串
單引號(hào)字符串
類似于 Python 中的原始字符串,也就是說(shuō)單引號(hào)字符串
沒(méi)有變量解析功能和特殊字符轉(zhuǎn)義功能。比如$str='hello\nworld'
剂跟,其中的\n
并沒(méi)有換行功能减途。
雙引號(hào)字符串
雙引號(hào)字符串
具備單引號(hào)字符串
沒(méi)有的變量解析功能和特殊字符轉(zhuǎn)義功能。
個(gè)人對(duì)于十六進(jìn)制和八進(jìn)制的字符串特殊轉(zhuǎn)義很感興趣曹洽,特別補(bǔ)充:
\[0-7]{1,3} #八進(jìn)制表達(dá)方式
\x[0-9A-Fa-f]{1,2} #十六進(jìn)制表達(dá)方式
heredoc
這種表達(dá)式類似于 Python 中的長(zhǎng)字符串鳍置,能夠定義包含多行的字符串。其語(yǔ)法定義很嚴(yán)格送淆,使用起來(lái)需要注意税产。
$str=<<<EOD
hello\n
world
EOD;
Nowdoc
Nowdoc類似于單引號(hào)字符串,不會(huì)解析變量偷崩。比較適合定義一大段文本且無(wú)需對(duì)其中的特殊字符進(jìn)行轉(zhuǎn)義辟拷。
變量解析
PHP字符串最強(qiáng)大的部分就是變量解析,可以在運(yùn)行時(shí)根據(jù)上下文解析變量(這才是解釋型語(yǔ)言)阐斜,可以產(chǎn)生很多妙用衫冻。
簡(jiǎn)單的變量解析就是在字符串中可以包含“變量”,“數(shù)組”谒出,“對(duì)象屬性”隅俘,復(fù)雜的語(yǔ)法規(guī)則就是使用{}
符號(hào)來(lái)進(jìn)行操作(組成一個(gè)表達(dá)式)。
通過(guò)一個(gè)例子看看變量解析的強(qiáng)大之處
class beers {
const softdrink = 'softdrink';
public static $ale = 'ale';
public $data = array(1,3,"k"=>4);
}
$softdrink = "softdrink";
$ale = "ale";
$arr = array("arr1","arr2","arr3"=>"arr4","arr4"=>array(1,2));
$arr4 = "arr4";
$obj = new beers;
echo "line1:{$arr[1]}\n";
echo "line2:{$arr['arr4'][0]}\n";
echo "line3:{$obj->data[1]}\n";
echo "line4:{${$arr['arr3']}}\n";
echo "line5:{${$arr['arr3']}[1]}\n";
echo "line6:{${beers::softdrink}}\n";
echo "line7:{${beers::$ale}}\n";
字符串轉(zhuǎn)換
PHP 語(yǔ)言比 Python 簡(jiǎn)單的另外一個(gè)原因就是類型的隱式轉(zhuǎn)換笤喳,會(huì)簡(jiǎn)化很多操作为居,這里通過(guò)字符串轉(zhuǎn)換來(lái)說(shuō)明。
字符串類型強(qiáng)制轉(zhuǎn)換
$var = 10 ;
$dvar = (string)$var ;
echo $dvar . "_" . gettype($dvar);
strval()函數(shù)是獲取變量的字符串值:
$var = 10.2 ;
$dvar = strval($var) ;
echo gettype($var) . "_" . $dvar . "_" . gettype($dvar);
settype()函數(shù)是設(shè)置變量的類型:
$str = "10hello";
settype($str, "integer");
echo $str ;
在強(qiáng)制類型轉(zhuǎn)換過(guò)程中杀狡,將其他類型的值轉(zhuǎn)換為字符串的時(shí)候會(huì)遵循一定的規(guī)則蒙畴,比如一個(gè)布爾值 boolean 的 TRUE 被轉(zhuǎn)換成 string 的 “1”。相關(guān)規(guī)則最好還是理解下呜象。
自動(dòng)類型轉(zhuǎn)換
上面的二個(gè)轉(zhuǎn)換屬于顯示轉(zhuǎn)換忍抽,而更要關(guān)注的是自動(dòng)類型轉(zhuǎn)換八孝,
在一個(gè)需要字符串的表達(dá)式中,會(huì)自動(dòng)轉(zhuǎn)換為類型鸠项,具體見(jiàn)例子:
$bool = true;
$str = 10 + "hello"
echo $bool . "_" . $str ;
PHP 字符串的本質(zhì)
引用 PHP 文檔的解釋:
PHP 中的 string 的實(shí)現(xiàn)方式是一個(gè)由字節(jié)組成的數(shù)組再加上一個(gè)整數(shù)指明緩沖區(qū)長(zhǎng)度干跛。并無(wú)如何將字節(jié)轉(zhuǎn)換成字符的信息,由程序員來(lái)決定祟绊。字符串由什么值構(gòu)成沒(méi)有限制楼入,包括值為 0 的字節(jié)可以出現(xiàn)在字符串的任何位置。
PHP并不特別指明字符串的編碼牧抽,那字符串到底是怎樣編碼的呢嘉熊,這取決于程序員。字符串會(huì)按照 PHP 文件的編碼來(lái)對(duì)字符串進(jìn)行編碼扬舒。比如你的文件編碼是 GBK阐肤,那么你代碼內(nèi)容都是 GBK 的。
補(bǔ)充二進(jìn)制安全這個(gè)概念讲坎,其值為 0 (NULL)的字節(jié)可以處于字符串任何位置孕惜,而 PHP 的部分非二進(jìn)制函數(shù)底層是調(diào)用的 C 函數(shù),會(huì)把 NULL 后面的字符忽略晨炕。
只要 PHP 的文件編碼是能兼容 ASCII 的衫画,那么字符串操作就可以很好的被處理。但是字符串操作本質(zhì)上還是 Native 的(不管文件編碼是什么)瓮栗,所以在使用的時(shí)候需要注意:
- 某些函數(shù)假定字符串是以單字節(jié)編碼的削罩,但并不需要將字節(jié)解釋為特定的字符。比如 sbustr() 函數(shù)费奸。
- 很多函數(shù)是需要顯示的傳遞編碼參數(shù)弥激,不然會(huì)從 PHP.INI 文件中獲取默認(rèn)值,比如 htmlentities() 函數(shù)愿阐。
- 還有一些函數(shù)和本地區(qū)域有關(guān)微服,這些函數(shù)也只能是單字節(jié)操作的。
一般情況下换况,雖然 PHP 內(nèi)部不支持 Unicode 字符,但是支持 UTF-8 編碼盗蟆,絕大部分情況下不會(huì)有什么問(wèn)題戈二,但是下列的情況可能就處理不了了:
- 非 UTF-8 編碼字符串如何進(jìn)行轉(zhuǎn)換
- 一個(gè) UTF-8 編碼的網(wǎng)頁(yè),但是用戶在提交表單的時(shí)候喳资,可能使用 GBK 的編碼(不遵守 meta tag)
- 一個(gè) UTF-8 編碼的 PHP 文件觉吭,使用 strlen("中國(guó)") 返回的是 6,而不是實(shí)際的字符數(shù)(2)
那么如何解決該問(wèn)題呢仆邓? PHP 提供了 mbstring 擴(kuò)展 鲜滩!
多字節(jié)字符串
mbstring 擴(kuò)展默認(rèn)不是打開(kāi)的伴鳖,安裝的時(shí)候需要 --enable-mbstring。
我們首先看看 PHP.INI 中對(duì)于 mbstring 指令的配置徙硅,花了好久才逐步明白榜聂。
- mbstring.language 這個(gè)參數(shù)我就理解為 UTF-8 了
- mbstring.internal_encoding 這個(gè)編碼和 PHP 文件編碼沒(méi)有關(guān)系,只是在大部分 mbstring 函數(shù)里面需要指定待處理字符串的編碼嗓蘑,假如不顯示指定须肆,默認(rèn)就獲取該參數(shù)的值,該參數(shù)的值在高版本 PHP 中用 default_charset 參數(shù)代替了桩皿。
- mbstring.http_input 該參數(shù)指定 HTTP input 的默認(rèn)編碼(不包含 GET 參數(shù))豌汇。一般和 HTML 頁(yè)面的編碼保持一致,該參數(shù)的值用 default_charset 參數(shù)代替泄隔。
- mbstring.http_output 該參數(shù)誤導(dǎo)我了拒贱,HTTP output 是什么,PHP 輸出不就是頁(yè)面佛嬉,怎么會(huì)有這概念逻澳?
- mbstring.encoding_translation,這個(gè)參數(shù)重點(diǎn)說(shuō)下巷燥,默認(rèn)是關(guān)閉的赡盘,假如打開(kāi),PHP 會(huì)對(duì) POST 變量和上傳文件的名稱自動(dòng)轉(zhuǎn)換編碼為 mbstring.internal_encoding 指定的值缰揪,不過(guò)我沒(méi)有試驗(yàn)過(guò)陨享,大家可以上傳一個(gè)中文名的文件試驗(yàn)下。建議關(guān)閉钝腺,讓程序員來(lái)處理相關(guān)問(wèn)題抛姑。
后面看看 mbstring 擴(kuò)展的一些函數(shù):
- mb_http_input():檢測(cè) HTTP input 字符編碼,覺(jué)得對(duì)于文件上傳的文件名有必要處理艳狐。
- mb_convert_encoding():比較常用的函數(shù)定硝,注意第三個(gè)參數(shù)。
- mb_detect_order():設(shè)置/獲取字符編碼的檢測(cè)順序毫目。
- mb_list_encodings():返回系統(tǒng)支持的編碼列表蔬啡。
重點(diǎn)說(shuō)明下:PHP 文件支持的編碼有一定要,要兼容 ASCII镀虐。
但是不要使用 BIG-5 作為 PHP 文件編碼箱蟆,尤其字符串以 identifiers 或 literals 形式出現(xiàn),假如 PHP 文件編碼一定要是 BIG-5刮便,那么對(duì)于輸入輸出的內(nèi)容盡量轉(zhuǎn)換為 UTF-8空猜。
Zend Multibyte
最后說(shuō)下 Zend Multibyte 這個(gè)概念,理解的不是特別深刻,首先不要和 mbstring 擴(kuò)展混在一塊辈毯。 Zend Multibyte 模式默認(rèn)是關(guān)閉的坝疼,可以通過(guò) zend.multibyte 指令打開(kāi)。然后通過(guò) declare() 函數(shù)來(lái)指定 PHP 解析器的編碼谆沃。
那這個(gè)指令出現(xiàn)的意義是什么钝凶?上面說(shuō)過(guò) PHP 文件的編碼需要是兼容 ASCII 的,那么類似于 BIG-5 這樣的非兼容 ASCII 編碼怎么辦管毙,可以通過(guò)這個(gè)指令來(lái)操作腿椎,當(dāng) PHP 解析器讀取 mbstring.script_encoding 編碼并用該編碼來(lái)解析 PHP 文件。