PHP中的字符串、編碼、UTF-8

最近看了不少編碼方面的文章,所以分二篇博文說下“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 文檔的解釋:

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 編碼字符串如何進行轉換
  • 一個 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

最后說下 Zend Multibyte 這個概念,理解的不是特別深刻,首先不要和 mbstring 擴展混在一塊。 Zend Multibyte 模式默認是關閉的,可以通過 zend.multibyte 指令打開。然后通過 declare() 函數來指定 PHP 解析器的編碼。

那這個指令出現的意義是什么?上面說過 PHP 文件的編碼需要是兼容 ASCII 的,那么類似于 BIG-5 這樣的非兼容 ASCII 編碼怎么辦,可以通過這個指令來操作,當 PHP 解析器讀取 mbstring.script_encoding 編碼并用該編碼來解析 PHP 文件。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容