最近看了不少編碼方面的文章,所以分二篇博文說下“PHP、字符串、編碼、UTF-8”相關知識,本篇博文是上半部分,分為四大塊內容,分別是“字串的定義與使用」、「字串轉換」、「PHP 字串的本質」、「多位元組字串」。上半部比較基礎,下一篇文章《PHP 與 UTF-8的最佳實踐》可能乾貨多一點。
# PHP 中能夠透過四種方法設定字串:
# 單引號字串
# 單引號字串類似於 Python 中的原始字串,也就是說單引號字串沒有變數解析功能和特殊字元轉義功能。例如$str='hello\nworld',其中的\n並沒有換行功能。
雙引號字串
# 雙引號字串具備單引號字串沒有的變數解析功能和特殊字元轉義功能。
個人對於十六進制和八進制的字串特殊轉義很感興趣,特別補充:
\[0-7]{1,3} #八进制表达方式 \x[0-9A-Fa-f]{1,2} #十六进制表达方式
heredoc
這種表達式類似於 Python 中的長字串,能夠定義包含多行的字串。其語法定義很嚴格,使用起來需要注意。
$str=<<<EOD hello\n world EOD;
Nowdoc
# Nowdoc類似單引號字串,不會解析變數。比較適合定義一大段文字且無需對其中的特殊字元進行轉義。
變數解析
PHP字串最強大的部分就是變數解析,可以在運行時根據上下文解析變數(這才是解釋型語言),可以產生很多妙用。
簡單的變數解析就是在字串中可以包含“變數”,“數組”,“物件屬性”,複雜的語法規則就是使用{}符號來進行操作(組成一個表達式)。
透過一個例子看看變數解析的強大之處
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";
PHP 語言比 Python 簡單的另一個原因就是類型的隱式轉換,會簡化很多操作,這裡透過字串轉換來說明。
字串型別強制轉換
$var = 10 ; $dvar = (string)$var ; echo $dvar . "_" . gettype($dvar);
strval()函數是取得變數的字串值:
$var = 10.2 ; $dvar = strval($var) ; echo gettype($var) . "_" . $dvar . "_" . gettype($dvar);
settype()函數是設定變數的型別:
$str = "10hello"; settype($str, "integer"); echo $str ;
在強制型別轉換過程中,將其他型別的值轉換為字串的時候會遵循一定的規則,例如一個布林值 boolean 的 TRUE 被轉換成 string 的 「1」。相關規則最好還是理解下。
自動型別轉換
上面的二個轉換屬於顯示轉換,而更要關注的是自動類型轉換,在一個需要字串的表達式中,會自動轉換為類型,具體見例子:
$bool = true; $str = 10 + "hello" echo $bool . "_" . $str ;
# 引用 PHP 文件的解釋:
PHP 中的 string 的實作方式是由位元組組成的陣列再加上一個整數指明緩衝區長度。並無如何將位元組轉換成字元的訊息,由程式設計師來決定。字串由什麼值構成沒有限制,包括值為 0 的位元組可以出現在字串的任何位置。
PHP並沒有特別指明字串的編碼,那字串到底是怎麼編碼的呢,這取決於程式設計師。字串會依照 PHP 檔案的編碼來對字串進行編碼。例如你的檔案編碼是 GBK,那你程式碼內容都是 GBK的。
# 補充二進位安全這個概念,其值為 0 (NULL)的位元組可以處於字串任何位置,而 PHP 的部分非二進位函數底層是呼叫的 C 函數,會把 NULL 後面的字元忽略。
只要 PHP 的檔案編碼是能相容 ASCII 的,那麼字串操作就可以很好的被處理。但是字串運算本質上還是 Native 的(不管檔案編碼是什麼),所以在使用的時候需要注意:
某些函數假定字串是以單字節編碼的,但並不需要將位元組解釋為特定的字元。如 sbustr() 函數。
# 很多函數是需要顯示的傳遞編碼參數,不然會從 PHP.INI 檔案中取得預設值,例如 htmlentities() 函數。
# 還有一些函數和本地區域有關,這些函數也只能是單字節操作的。
一般情況下,雖然PHP 內部不支援Unicode 字符,但支援UTF-8 編碼,絕大部分情況下不會有什麼問題,但是下列的情況可能就處理不了了:
# 一個UTF-8 編碼的網頁,但是使用者在提交表單的時候,可能使用GBK 的編碼(不遵守meta tag)
一個 UTF-8 編碼的 PHP 文件,使用 strlen("中國")傳回的是 6 而不是實際的字元數(2)
那麼如何解決該問題呢? PHP 提供了 mbstring 擴充功能 !
mbstring 擴充預設不是開啟的,安裝的時候需要 --enable-mbstring。
我們先來看看 PHP.INI 中對於 mbstring 指令的配置,花了好久才逐步明白。
mbstring.language 這個參數我就理解為 UTF-8 了
mbstring.internal_encoding 這個編碼和PHP 檔案編碼沒有關係,只是在大部分mbstring 函數裡面需要指定待處理字串的編碼,假如不顯示指定,預設就取得該參數的值,該參數的值在高版本PHP 中用default_charset 參數代替了。
# mbstring.http_input 此參數指定 HTTP input 的預設編碼(不包含 GET 參數)。一般和 HTML 頁面的編碼保持一致,該參數的值以 default_charset 參數取代。
# mbstring.http_output 這個參數誤導我了,HTTP output 是什麼,PHP 輸出不就是頁面,怎麼會有這概念?
# mbstring.encoding_translation,這個參數重點說下,預設是關閉的,假如打開,PHP 會對POST 變數和上傳檔案的名稱自動轉換編碼為mbstring.internal_encoding 指定的值,不過我沒有試驗過,大家可以上傳一個中文名的文件。建議關閉,讓程式設計師來處理相關問題。
後面來看看 mbstring 擴充的一些函數:
# mb_http_input():偵測 HTTP input 字元編碼,覺得對於檔案上傳的檔案名稱有必要處理。
# mb_convert_encoding():比較常用的函數,注意第三個參數。
# mb_detect_order():設定/取得字元編碼的偵測順序。
# mb_list_encodings():傳回系統支援的編碼清單。
重點說明下:PHP 檔案支援的編碼有一定要,要相容 ASCII。
但不要使用 BIG-5 作為 PHP 檔案編碼,尤其字串以 identifiers 或 literals 形式出現,假如實在 PHP 檔案編碼要是 BIG-5,那麼對於輸入輸出的內容盡量轉換為 UTF-8。
# 最後說下 Zend Multibyte 這個概念,理解的不是特別深刻,首先不要和 mbstring 擴展混在一塊。 Zend Multibyte 模式預設是關閉的,可以透過 zend.multibyte 指令開啟。然後透過 declare() 函數來指定 PHP 解析器的編碼。
那這個指令出現的意義是什麼?上面說過PHP 檔案的編碼需要是相容ASCII 的,那麼類似於BIG-5 這樣的非相容ASCII 編碼怎麼辦,可以透過這個指令來操作,當PHP 解析器讀取mbstring.script_encoding 編碼並用該編碼來解析PHP 檔案。
以上就是PHP中的字串、編碼、UTF-8的程式碼詳解的內容,更多相關內容請關注PHP中文網(www.php.cn)!