PHP 開發工程師基礎篇 - PHP 字串

it阿布發表於2020-08-11

字串 (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

語法

  1. 單引號
  2. 雙引號
  3. heredoc 語法結構
  4. 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;

PHP7.3.0 字串分析

第 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;

PHP7.3.0 字串分析1

第 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

相關文章