在 PHP 中获取字符串长度时,strlen 和 mb_strlen 是两个最常被提到的函数。表面上看它们都在计算长度,但实际返回结果可能差异很大,尤其是字符串里包含中文、emoji 或者混合了多字节字符时。本文将从字节与字符的底层差异讲起,逐步说明两个函数的适用场景、参数配置、常见误区以及安全封装方案。

strlen 与 mb_strlen 的核心差异
PHP 的字符串本质上是字节序列,并不天然理解 Unicode 字符。内置函数 strlen 直接统计字符串占用的字节数,而不是用户视角下的字符个数。对于纯 ASCII 字符,例如英文字母、数字、常见标点,每个字符固定占 1 个字节,因此 strlen 的结果和字符数一致。例如字符串 hello 返回 5,不会引起歧义。
一旦字符串中出现中文、日文、emoji 等多字节字符,strlen 的结果就会与肉眼看到的字符数量产生偏差。以 UTF-8 编码为例,一个常用汉字占 3 个字节,一个 emoji 通常占 4 个字节。此时 strlen 返回的是字节总数,而 mb_strlen 才会按照字符个数进行统计。下面这行代码可以直观看到差异:
<?php $asciiStr = 'hello'; $utf8Str = '你好'; echo strlen($asciiStr); // 输出 5 echo strlen($utf8Str); // 输出 6,UTF-8 下每个汉字占 3 字节 ?>
mb_strlen 是 mbstring 扩展提供的多字节字符串函数。它不再简单统计字节数,而是根据指定编码识别出一个个字符,再返回字符数量。对于同样的 你好,如果指定 UTF-8 编码,返回值是 2。因此,只要项目里可能处理中文或其他多字节字符,就应该优先考虑 mb_strlen。
两者的差异可以概括为:strlen 关心的是存储层用了多少字节,mb_strlen 关心的是语义层有多少个字符。这个区别不仅影响长度判断,还直接影响字符串截取、分页、搜索、排序等后续操作。
为什么中文字符串长度会出现差异
要理解长度差异,需要先了解常见字符编码。ASCII 字符总是占 1 个字节,GBK 编码下中文通常占 2 个字节,UTF-8 编码下中文大多占 3 个字节。因此同样的中文字符串,在不同编码环境里,strlen 的返回值可能完全不同。假设字符串为 中文abc,在 UTF-8 下,两个汉字占 6 个字节,三个字母占 3 个字节,strlen 总计返回 9;而 mb_strlen 按字符计算则返回 5。
<?php $str = '中文abc'; echo strlen($str); // UTF-8 下输出 9 echo mb_strlen($str, 'UTF-8'); // 输出 5 ?>
这种差异会对实际业务造成直接影响。例如用户昵称限制为 10 个字符,如果使用 strlen 判断,一个包含 4 个汉字的昵称可能在 UTF-8 下被计算为 12,从而被错误拒绝。再比如使用 substr 按字节截取中文字符串,很容易从汉字中间切开,导致最后出现乱码。正确的做法是使用 mb_strlen 判断长度,用 mb_substr 进行截取,并保证编码参数一致。
还需要注意,如果 mb_strlen 指定的编码与字符串实际编码不一致,统计结果也可能异常。例如一个 UTF-8 字符串被错误地按 GBK 解释,函数可能识别出错误的字符边界。因此,调用 mb_strlen 时最好显式传入编码参数,而不是依赖服务器默认配置。
mb_strlen 的参数用法与扩展配置
mb_strlen 的基本签名是 mb_strlen(string $str, ?string $encoding = null): int。第一个参数是要计算的字符串,第二个参数是字符编码。如果不传第二个参数,函数会使用 mbstring 扩展的内部编码,该编码可以通过 mb_internal_encoding 函数进行设置。
<?php
mb_internal_encoding('UTF-8');
$str = '你好';
echo mb_strlen($str); // 不传第二个参数时使用内部编码,输出 2
?>
推荐的写法是每次调用都显式传入编码,例如 mb_strlen($str, 'UTF-8')。这样做的好处是代码行为不依赖 php.ini 配置,迁移到不同服务器时结果仍然一致。常见的编码取值包括 UTF-8、GBK、BIG5、ISO-8859-1 等。如果项目统一使用 UTF-8,那么全部传 UTF-8 是最稳妥的方式。
mbstring 扩展默认通常已经启用,但部分精简版 PHP 环境可能没有安装。调用 mb_strlen 之前,可以使用 function_exists 进行检测。如果扩展不存在,程序会直接抛出致命错误,而不是返回 false。对于需要兼容多种运行环境的项目,这个检测步骤非常重要。
另一个值得注意的配置是 php.ini 中的 mbstring.func_overload。在较老的 PHP 版本中,该配置可以重载部分字符串函数,使 strlen 自动表现为多字节版本。不过从 PHP 7.2 开始,这项配置已被废弃,在 PHP 8 中已完全移除。现代项目不应依赖这种全局重载,而应显式调用 mb_strlen。
实战封装一个兼容多字节的长度函数
为了兼顾代码可读性和运行环境兼容性,可以封装一个安全的字符串长度函数。优先使用 mb_strlen,如果扩展不可用,则退回到正则方案或普通 strlen。下面是一个常见封装示例:
<?php
function safeStrlen(string $str, string $encoding = 'UTF-8'): int
{
if (function_exists('mb_strlen')) {
return mb_strlen($str, $encoding);
}
// 降级方案:使用正则按 Unicode 字符拆分后统计
if (preg_match_all('/./us', $str, $matches)) {
return count($matches[0]);
}
return strlen($str);
}
$content = 'PHP字符串长度测试';
echo safeStrlen($content); // 输出字符数量
?>
这个函数首先检查 mb_strlen 是否可用,如果可用就直接返回多字节字符数量。否则使用 preg_match_all('/./us', $str, $matches) 将字符串按 Unicode 字符拆分,u 修饰符启用 UTF-8 模式,s 修饰符让点号也能匹配换行符。统计匹配数组的元素个数,同样可以得到字符数量。最后再退回普通 strlen,虽然对中文不够准确,但至少可以保证函数始终有返回值。
这种封装特别适合用于用户输入校验。例如限制用户昵称在 4 到 20 个字符之间,或者限制评论内容不超过 500 个字符。直接调用 safeStrlen 就能避免中文被错误地按 3 倍字节数计算。同理,如果需要截取摘要,应该配套使用 mb_substr 或类似的多字节截取函数,确保不会从字符中间断开。
常见误区与性能选择建议
很多开发者遇到 strlen 返回中文长度偏大时,会习惯性地将结果除以 3,试图还原字符数量。这种方式在纯中文、纯 UTF-8 编码下看似可行,但一旦字符串混合了英文、数字、空格或 emoji,就会立刻出错。例如 中文abc 的字节数是 9,但字符数是 5,既不能除以 3,也不能简单除以 2。与其维护脆弱的换算逻辑,不如从一开始就使用 mb_strlen。
从性能角度看,strlen 直接读取字符串的字节长度,速度极快;mb_strlen 需要解析编码、识别字符边界,因此会稍慢一些。对于纯 ASCII 字符串,例如订单号、验证码、加密串等场景,直接使用 strlen 完全足够。而涉及用户输入、文章内容、多语言文案时,应优先使用 mb_strlen。两者性能差异在绝大多数业务中都可以忽略,不应为了微小的性能提升而牺牲准确性。
<?php
$str = str_repeat('你好a', 10000);
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$len = strlen($str);
}
$end = microtime(true);
echo 'strlen 耗时:' . ($end - $start) . ' 秒' . PHP_EOL;
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$len = mb_strlen($str, 'UTF-8');
}
$end = microtime(true);
echo 'mb_strlen 耗时:' . ($end - $start) . ' 秒' . PHP_EOL;
?>
总体建议是:项目如果已经统一使用 UTF-8 编码,并且存在任何中文、emoji 或国际化输入,就始终使用 mb_strlen;只有在确认字符串只包含 ASCII 字符时,才使用 strlen。同时要保证编码参数、数据库连接编码、页面输出编码保持一致,否则长度判断、截取和存储都会互相影响。
回到最初的问题:PHP 字符串长度怎么获取,并没有唯一答案,而是取决于你关注的是字节数还是字符数。弄清楚 strlen 与 mb_strlen 的区别,才能在表单校验、内容截取和接口字段限制等场景中写出没有乱码、符合预期的 PHP 代码。