PHP Conference Japan 2024

mb_substr

(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)

mb_substr取得字串的一部分

說明

mb_substr(
    字串 $string,
    整數 $start,
    ?整數 $length = null,
    ?字串 $encoding = null
): 字串

根據字元數執行多位元組安全的 substr() 操作。位置從 string 的開頭開始計算。第一個字元的位置是 0。第二個字元的位置是 1,依此類推。

參數

字串

要擷取子字串的 字串

起始位置

如果 start 非負數,則返回的字串將從 string 中的第 start 個位置開始,從零開始計數。例如,在字串 'abcdef' 中,位置 0 的字元是 'a',位置 2 的字元是 'c',依此類推。

如果 start 為負數,則返回的字串將從 string 的末尾開始的第 start 個字元開始。

長度

要從 string 中使用的最大字元數。如果省略或傳遞 NULL,則擷取到字串結尾的所有字元。

編碼

encoding 參數是字元編碼。如果省略或傳遞 null,則將使用內部字元編碼值。

回傳值

mb_substr() 返回由 startlength 參數指定的 string 的部分。

更新日誌

版本 說明
8.0.0 encoding 現在可以為 null。

另請參閱

新增註釋

使用者貢獻的註釋 9 則註釋

qbolec at gmail dot com
9 年前
由於您經常需要逐一處理字串內的 UTF-8 字元,您可能會想使用 mb_substr($text,$i,1)。
這樣做的問題是,除了從頭開始逐位元組讀取之外,沒有「神奇」的方法可以在 UTF-8 字串中找到第 $i 個字元。因此,對於所有可能的 $i 值 N 次呼叫 mb_substr($text,$i,1) 的迴圈,將比預期花費更長的時間。$i 越大,搜尋第 $i 個字母的時間就越長。由於字元長度介於 1 到 6 個位元組之間,因此可以說服自己,此類迴圈的執行時間實際上是 Theta(N^2),即使對於中等長度的文字也可能非常慢。
解決此問題的一種方法是先使用一些巧妙的預處理將文字拆分為字母陣列,然後再逐一處理陣列。
以下是這個想法
<?php
class Strings
{
public static function
len($a){
return
mb_strlen($a,'UTF-8');
}
public static function
charAt($a,$i){
return
self::substr($a,$i,1);
}
public static function
substr($a,$x,$y=null){
if(
$y===NULL){
$y=self::len($a);
}
return
mb_substr($a,$x,$y,'UTF-8');
}
public static function
letters($a){
$len = self::len($a);
if(
$len==0){
return array();
}else if(
$len == 1){
return array(
$a);
}else{
return
Arrays::concat(
self::letters(self::substr($a,0,$len>>1)),
self::letters(self::substr($a,$len>>1))
);
}
}
?>
如您所見,`Strings::letters($text)` 函式會遞迴地將文字分成兩部分。每個遞迴層級都需要線性時間來處理字串長度,並且遞迴層級的數量是對數級別的,因此總執行時間為 O(N log N),這仍然比理論上最佳的 O(N) 要多,但遺憾的是,這是我想到的最佳方法。
drraf at tlen dot pl
19 年前
注意:如果邊界超出字串範圍,`mb_string()` 函式會返回空字串,而 `substr()` 函式在此情況下會返回布林值 `false`。
使用 「===」 比較時請記住這一點。

範例程式碼
<?php

var_dump
( substr( 'abc', 5, 2 ) ); // 回傳 "false"
var_dump( mb_substr( 'abc', 5, 2 ) ); // 回傳 ""

?>

在啟用函式重載的情況下搭配使用 mbstring 時,會特別令人困惑。
p dot assenov at aip-solutions dot com
13 年前
我試圖只將字串的第一個字元大寫,並嘗試了上面的一些範例,但它們沒有作用。似乎 mb_substr() 無法計算多位元組編碼 (UTF-8) 中的字串長度,應該明確設定。以下是修正後的版本

<?php
function mb_ucfirst($str, $enc = 'utf-8') {
return
mb_strtoupper(mb_substr($str, 0, 1, $enc), $enc).mb_substr($str, 1, mb_strlen($str, $enc), $enc);
}
?>

乾杯!
xiaogil at yahoo dot fr
19 年前
感謝 /freenode #php 的 Darien 提供以下範例(略有修改)。

它只印出 $string 的第 6 個字元。
您可以將數字替換成日文、中文或任何語言的相同數字來進行測試,它可以完美運作。

<?php
mb_internal_encoding
("UTF-8");
$string = "0123456789";
$mystring = mb_substr($string,5,1);
echo
$mystring;
?>

(我在這裡無法將 0123456789 替換為例如中文數字,因為它會在此網站上自動轉換為拉丁數字,請參見
&#38646;&#19968;&#20108;&#19977;&#22235;
&#20116;&#20845;&#19971;&#20843;&#20061;)

gilv
public at luedi dot jp
8 個月前
只想補充一點,不僅 `start` 可以是負數,`length` 也可以是負數。而且它的作用符合預期。

mb_substr( "1234567890", 3, -4, "UTF-8" ) => "456"。

所以它會截掉最後 4 個字元。
boulahdidraid18 at gmail dot com
11 個月前
以下範例示範了 `substr` 和 `mb_substr` 函式的差異

1- 使用非 UTF-8 字元時,兩個函式的行為相同,並提供相同的輸出

$str = 'abcdef';
echo substr($s, 0, 3); // abc
echo mb_substr($s, 0, 3); // abc

2- 使用 UTF-8 字元時,每個函式的行為將有所不同,並提供不同的結果

2.A- `substr` 函式作用於位元組層級,並且僅適用於單位元組編碼的字元(不支援多位元組編碼)。

例如

$str_utf8 = utf8_encode("déjà_vu");

如果我們這樣做

echo substr($str_utf8, 0, 3); // dé
echo substr($str_utf8, 0, 2); // d�

=> 這是因為特殊字元「é」(和「à」)在內部是以兩個位元組編碼的

PHP 會從索引 0 開始讀取第一個位元組,它代表 `d`,然後移至第二個位元組,它是字元 `é` 的雙位元組編碼的一部分,由於長度設定為 2,PHP 將在此停止且不會繼續讀取第三個位元組,因此它無法辨識字元 `é` 並印出 � 來代替 é。

2.B- `mb_substr` 函式作用於字元層級,並且支援多位元組編碼的字元。這表示 PHP 只計算字元數,而不考慮其編碼的位元組數,例如

$str_utf8 = utf8_encode("déjà_vu");

echo mb_substr($str_utf8, 0, 4, "UTF-8"); // Déjà
echo mb_substr($str_utf8, 1, 4, "UTF-8"); // éjà_
echo mb_substr($str_utf8, 6, 4, "UTF-8"); // u
echo mb_substr($str_utf8, 7, 4, "UTF-8"); // ''
echo mb_substr($str_utf8, -2, "UTF-8"); // vu
echo mb_substr($str_utf8, -2, 1, "UTF-8"); // v
echo mb_substr($str_utf8, -2, 3, "UTF-8"); // vu
desmatic at gmail dot com
11 年前
快速且簡便地迴圈處理多位元組字串
<?php
function get_character_classes($string, $encoding = "UTF-8") {
$current_encoding = mb_internal_encoding();
mb_internal_encoding($encoding);
$has = array();
$stringlength = mb_strlen($string, $encoding);
for (
$i=0; $i < $stringlength; $i++) {
$c = mb_substr($string, $i, 1);
if ((
$c >= "0") && ($c <= "9")) {
$has['numeric'] = "numeric";
} else if ((
$c >= "a") && ($c <= "z")) {
$has['alpha'] = "alpha";
$has['alphalower'] = 'alphalower';
} else if ((
$c >= "A") && ($c <= "Z")) {
$has['alpha'] = "alpha";
$has['alphaupper'] = "alphaupper";
} else if ((
$c == "$") || ($c == "£")) {
$has['currency'] = "currency";
} else if ((
$c == ".") && ($has['decimal'])) {
$has['decimals'] = "decimals";
} else if (
$c == ".") {
$has['decimal'] = "decimal";
} else if (
$c == ",") {
$has['comma'] = "comma";
} else if (
$c == "-") {
$has['dash'] = "dash";
} else if (
$c == " ") {
$has['space'] = "space";
} else if (
$c == "/") {
$has['slash'] = "slash";
} else if (
$c == ":") {
$has['colon'] = "colon";
} else if ((
$c >= " ") && ($c <= "~")) {
$has['ascii'] = "ascii";
} else {
$has['binary'] = "binary";
}
}
mb_internal_encoding($current_encoding);

return
$has;
}

$string = "1234asdfA£^_{}|}~žščř";
echo
print_r(get_character_classes($string), true);
?>

陣列
(
[numeric] => 數字
[alpha] => 字母
[alphalower] => 小寫字母
[alphaupper] => 大寫字母
[currency] => 貨幣
[ascii] => ASCII
[binary] => 二進位
)
sanjuro at 1up-games dot com
11 年前
使用設定為 HTML-ENTITIES 編碼的 mb_substr() 時,一個嚴重的陷阱是該函式在傳回值之前會執行許多轉換,其中最糟糕的是 HTML 特殊字元不僅會被計數,還會被解碼。

<?php

mb_internal_encoding
("ISO-8859-1"); echo mb_internal_encoding(),"\n<br><br>\n";

$a='j&uuml;st &#228; &quot; simple &quot; &#26085;&#26412; &lt;b&gt;test&lt;/b&gt;';

echo
mb_substr($a,0),"\n<br><br>\n";
// 頁面原始碼:j&uuml;st &#228; &quot; simple &quot; &#26085;&#26412; &lt;b&gt;test&lt;/b&gt;

echo mb_substr($a,0,strlen($a),'HTML-ENTITIES');
// 頁面原始碼:j&uuml;st &auml; " simple " &#26085;&#26412; <b>test</b>

?>
qdinar at gmail dot com
8 年前
您可以使用 ucs-2 編碼讓 mb_substr 在處理長字串時速度更快。

<?php

header
('Content-Type: text/html; charset=utf-8');
echo
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >';

function
test($string, $encoding='utf8'){
$t1=microtime(true);
$textlen=mb_strlen($string);
$substr_len=3;
for(
$i=0;$i<$textlen-$substr_len+1;$i++){
$substr=mb_substr($string,$i,$substr_len);
}
echo
'mb_substr, '.$encoding.': '.(microtime(true)-$t1);
echo
' . 檢查:';
if(
$encoding=='ucs2'){
$substr=mb_convert_encoding($substr,'utf-8','ucs2');
}
var_dump( $substr );
echo
' . <br>';
echo
'<br>';
}

$corpus_short=str_repeat('тест Тест ',1000);
// 使用 utf8 編碼的 "test Test" 執行速度同樣緩慢
mb_internal_encoding('utf-8');
test($corpus_short);

$corpus_short_ucs2=mb_convert_encoding($corpus_short,'ucs2','utf-8');
mb_internal_encoding('ucs2');
test($corpus_short_ucs2,'ucs2');

?>

輸出

mb_substr, utf8: 0.26480984687805 . 檢查:string(5) "ст " .

mb_substr, ucs2: 0.0048871040344238 . 檢查:string(5) "ст " .
To Top