PHP Conference Japan 2024

mb_substr

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

mb_substr获取字符串的一部分

描述

mb_substr(
    string $string,
    int $start,
    ?int $length = null,
    ?string $encoding = null
): string

基于字符数执行多字节安全的 substr() 操作。位置从 string 的开头算起。第一个字符的位置是 0。第二个字符的位置是 1,依此类推。

参数

string

要从中提取子字符串的 string

start

如果 start 非负,则返回的字符串将从 string 中的第 start 个位置开始,从零开始计数。例如,在字符串“abcdef”中,位置 0 处的字符是“a”,位置 2 处的字符是“c”,依此类推。

如果 start 为负数,则返回的字符串将从 string 末尾的第 start 个字符开始。

length

string 中使用的最大字符数。如果省略或传递 NULL,则提取字符串末尾的所有字符。

encoding

encoding 参数是字符编码。如果省略或为 null,则将使用内部字符编码值。

返回值

mb_substr() 返回由 startlength 参数指定的 string 的一部分。

变更日志

版本 描述
8.0.0 encoding 现在可以为空。

参见

添加注释

用户贡献的注释 9 条注释

qbolec at gmail dot com
9 年前
由于您经常需要迭代字符串内的 UTF-8 字符,因此您可能会尝试使用 mb_substr($text,$i,1)。
这样做的问题在于,没有“神奇”的方法可以在 UTF-8 字符串中查找第 $i 个字符,除非从开头逐字节读取。因此,对于所有可能的 $i 的 N 个值,对所有可能的 N 个值调用 mb_substr($text,$i,1) N 次的循环将花费比预期更长的时间。$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] => numeric
[alpha] => alpha
[alphalower] => alphalower
[alphaupper] => alphaupper
[currency] => currency
[ascii] => ascii
[binary] => 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
' . check: ';
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 . check: string(5) "ст " .

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