字串 (String)
字串是一系列字元的集合。如 “abc”. 在 PHP 中,一個字元代表一個位元組,一個位元組 (Byte) 有 8 位元 (bit). PHP 僅支援 256 字符集,因此 PHP 本身不支援 Unicode 字符集.
This means that PHP only supports a 256-character set, and hence does not offer native Unicode support.
那麼 PHP 是如何做到支援 Unicode 字符集的,答案是在於 PHP 底層字串實現中,PHP 的字串是一個由位元組組成的陣列再加上一個整數指明緩衝區長度。並沒有如何將位元組轉換字元的資訊。那麼字串如何編碼和解碼,這些全部由使用者來決定編碼方式.
字串會被按照該指令碼檔案相同的編碼方式來編碼。因此如果一個指令碼的編碼是 ISO-8859-1,則其中的字串也會被編碼為 ISO-8859-1,以此類推。不過這並不適用於啟用了 Zend Multibyte 時;此時指令碼可以是以任何方式編碼的(明確指定或被自動檢測)然後被轉換為某種內部編碼,然後字串將被用此方式編碼存入由位元組組成的陣列.
The answer is that string will be encoded in whatever fashion it is encoded in the script file. Thus, if the script is written in ISO-8859-1, the string will be encoded in ISO-8859-1 and so on. However, this does not apply if Zend Multibyte is enabled; in that case, the script may be written in an arbitrary encoding (which is explicitly declared or is detected) and then converted to a certain internal encoding, which is then the encoding that will be used for the string literals.
通過例子說明一下,比較易懂.
// unicode 字符集下 utf-8 編碼方式 $s = '嚴'; var_dump(strlen($s)); // 3 $len = strlen($s); // 3 /** * int(228) 對應二進位制為 11100100 * int(184) 對應二進位制為 10111000 * int(165) 對應二進位制為 10100101 */ for ($i = 0; $i < $len; $i++) { var_dump(ord($s[$i])); }
“嚴” 這個字怎麼會是 3 個,而不是一個呢,由於 strlen 返回的是位元組數,而非字元數. PHP 底層字串實現方式。是由位元組組成的陣列的。因此可以得出 PHP 對於 “嚴” 在 utf-8 編碼方式的底層實現是由 3 個位元組組成陣列,而這些位元組不超過 256 的值,這就是 PHP 編碼方式。而解碼則是通過檔案編碼方式取對應位元組數目來找對應字面字元.
而這些數值代表什麼意思,則是 utf-8 的編碼方式了。嚴的Unicode 是 4E25(100111000100101),根據上表,可以發現 4E25 處在第三行的範圍內(0000 0800 - 0000 FFFF),因此嚴的 UTF-8 編碼需要三個位元組,即格式是 1110xxxx 10xxxxxx 10xxxxxx。然後,從嚴的最後一個二進位制位開始,依次從後向前填入格式中的 x,多出的位補 0。這樣就得到了,嚴的 UTF-8 編碼是 11100100 10111000 10100101,轉換成十六進位制就是 E4B8A5。
非二進位制安全
字串由什麼值來組成並無限制;特別的,其值為 0(“NUL bytes”)的位元組可以處於字串任何位置 (不過有幾個函式,在本手冊中被稱為非 “二進位制安全” 的,也許會把 NUL 位元組之後的資料全都忽略). 在 C 語言裡字串都以 \0 作為結束符,而 PHP 底層是由 C 語言編寫的,因此某些函式遇到 \0 的 會忽略之後位元組的,從而影響程式邏輯和結果.
$string1 = "Hello"; $string2 = "Hello\0World"; // 非二進位制安全, \0 在 ascii 表是 NUL '\0' var_dump(strcoll($string1, $string2)); // 0 // 二進位制安全 var_dump(strcmp($string1, $string2)); // -6
語法
- 單引號
- 雙引號
- heredoc 語法結構
- nowdoc 語法結構 (自 PHP 5.3.0 起)
單引號
定義一個字串的最簡單的方法是用單引號把它包圍起來 (字元 '), 單引號無法解析變數和轉義字元,要表達單引號本身則在單引號前面加個反斜槓 (), 要表達反斜槓 () 則在反斜槓前加個反斜槓 ()
// this is string echo 'this is string'; // I'm your father echo 'I\'m your father'; // C:\*.*? echo 'C:\\*.*?';
雙引號
用雙引號 (") 把字元包圍起來,PHP 雙引號會把特殊字元解釋以下轉義序列.
雙引號還有一個特點,就是會解析變數。變數解析有兩個解析語法,一個是簡單和一個複雜的.
- 簡單解析語法:
簡單的解析方式當 PHP 解析器遇到一個美元符號($)時,它會和其它很多解析器一樣,去組合儘量多的標識以形成一個合法的變數名。可用花括號限制解析範圍.
$juice = 'apple'; $juices = 'apple\s'; // He drank some apple juice echo "He drank some $juice juice." . PHP_EOL; // He drank some apple\s juice echo "He drank some $juices juice" . PHP_EOL; // He drank some apples juice echo "He drank some {$juice}s juice" . PHP_EOL;
類似的,一個 array 索引或一個 object 屬性也可被解析。陣列索引要用方括號(])來表示索引結束的邊際,物件屬性則是和上述的變數規則相同。
- 複雜解析語法
這並不因為語法複雜而被稱為複雜,而是因為它允許使用複雜的表示式.
任何具有 string 表達的標量變數,陣列單元或物件屬性都可使用此語法。只需簡單地像在 string 以外的地方那樣寫出表示式,然後用花括號 {和} 把它括起來即可。由於 { 無法被轉義,只有 $ 緊挨著 { 時才會被識別。可以用 {$ 來表達 {$。
echo "This works too: {$obj->values[3]->name}"; echo "This works too: {$obj->values[3]->name}"; echo "This is the value of the var named by the return value of getName(): {${getName()}}";
Heredoc
第三種表達字串的方法是用 heredoc 句法結構:<<<。在該運算子之後要提供一個識別符號,然後換行。接下來是字串 string 本身,最後要用前面定義的識別符號作為結束標誌。
結束時所引用的識別符號必須在該行的第一列,而且,識別符號的命名也要像其它標籤一樣遵守 PHP 的規則:只能包含字母、數字和下劃線,並且必須以字母和下劃線作為開頭。
Heredoc 結構就象是沒有使用雙引號的雙引號字串,這就是說在 heredoc 結構中單引號不用被轉義,但是上文中列出的轉義序列還可以使用
$str = <<<EOD Example of string spanning multiple lines using heredoc syntax. EOD; $juice = 'apple'; $str1 = <<<EOD He drank some $juice juice. EOD; // 運用在函式中 function foo() { static $bar = <<<LABEL Nothing in here... LABEL; } // 運用在類中 class foo { const BAR = <<<FOOBAR Constant example FOOBAR; public $baz = <<<FOOBAR Property example FOOBAR; }
Nowdoc
Nowdoc 結構很象 heredoc 結構,但是 nowdoc 中不進行解析操作。這種結構很適合用於嵌入 PHP 程式碼或其它大段文字而無需對其中的特殊字元進行轉義
一個 nowdoc 結構也用和 heredocs 結構一樣的標記 <<<, 但是跟在後面的識別符號要用單引號括起來,即 <<<‘EOT’。Heredoc 結構的所有規則也同樣適用於 nowdoc 結構,尤其是結束識別符號的規則。
$str = <<<'EOD' Example of string spanning multiple lines using nowdoc syntax. EOD;
單引號比雙引號快?
在 PHP 7 之前,假如字串沒有存在變數替換,單引號和雙引號幾乎一樣的。如果存在變數替換的時候是單引號比雙引號要快. PHP 7 之後,卻是不同,雙引號與單引號幾乎一樣效能.
$singleCode = 'this is single string'; $doubleCode = "this is single string"; echo $singleCode; echo $doubleCode;
第 0 到第 3 條 op line, 可以看出在沒有使用變數替換的情況下,雙引號的和單引號所產生的 Opcodes 是一樣的.
$var = 'String'; $single_quotes_var = 'This is a '.$var; $double_quotes_var = "This is a $var"; echo $single_quotes_var; echo $double_quotes_var;
第 1 到 2 條與第 4 到 5 條,Opcodes 條數是一樣的. PHP 7 把之前 INIT_STRING, ADD_STRING, ADD_VAR 的步驟優化成一條。優化成 FAST_CONCAT
存取和修改字串中的字元
string 中的字元可以通過一個從 0 開始的下標,用類似 array 結構中的方括號包含對應的數字來訪問和修改,比如 $str [42]。可以把 string 當成字元組成的 array. 也可以使用花括號來訪問和修改 注意的是字串操作最好針對單位元組編碼操作,因為字串在 PHP 底層是位元組組成的陣列,用花括號 / 方括號操作多位元組字串很不安全.
在 PHP7.1.0, 還支援負字串偏移量
用超出字串長度的下標寫入將會拉長該字串並以空格填充。非整數型別下標會被轉換成整數。
$str = 'This is a test.'; $first = $str[0]; $third = $str{2}; $last = $str[-1]; echo $first . PHP_EOL; // T echo $third . PHP_EOL; // i echo $last . PHP_EOL; // . $len = strlen($str); $str[$len+1] = 'a'; echo $str . PHP_EOL; // This is a test. a $str1 = "我"; echo ord($str1[0]); // 230
還有更多進階學習資料領取進階PHP月薪30k>>>架構師成長路線【視訊、面試文件免費獲取】