直接使用 substr() 處理 UTF-8 中文會切斷位元組,產生亂碼。正確做法需依編碼採用多位元組感知函式,並在缺乏擴展時提供相容備援。
<?php
/**
* 多位元組安全字串截取
*
* @param string $str 原始字串
* @param int $start 起始位置(字元索引,0 起算)
* @param int $length 截取長度
* @param string $charset 編碼:utf-8, gb2312, gbk, big5
* @param bool $suffix 是否附加省略符號
* @return string
*/
function msubstr(string $str, int $start, int $length, string $charset = 'utf-8', bool $suffix = true): string
{
// 1. 優先使用 mbstring 擴展
if (function_exists('mb_substr')) {
$slice = mb_substr($str, $start, $length, $charset);
}
// 2. 次選 iconv 擴展
elseif (function_exists('iconv_substr')) {
$slice = iconv_substr($str, $start, $length, $charset);
if ($slice === false) $slice = '';
}
// 3. 純 PHP 正則備援(無擴展環境)
else {
$patterns = [
'utf-8' => '/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/',
'gb2312' => '/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/',
'gbk' => '/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/',
'big5' => '/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/',
];
$pattern = $patterns[$charset] ?? $patterns['utf-8'];
preg_match_all($pattern, $str, $matches);
$slice = implode('', array_slice($matches[0], $start, $length));
}
return $suffix ? $slice . '...' : $slice;
}
$text = 'PHP 多國語言字串截取測試:你好世界!';
// UTF-8 環境
echo msubstr($text, 0, 15); // PHP 多國語言字串截取...
echo msubstr($text, 5, 10, 'utf-8'); // 語言字串截取測試...
echo msubstr($text, 0, 20, 'utf-8', false); // 無省略號
// GBK 編碼資料(需先 iconv 轉碼或指定 charset)
$gbkText = iconv('utf-8', 'gbk', $text);
echo msubstr($gbkText, 0, 10, 'gbk');
| 方案 | 速度 | 相依性 | 建議場景 |
|---|---|---|---|
| mb_substr | 最快 | mbstring | 生產環境首選 |
| iconv_substr | 快 | iconv | 無 mbstring 時備援 |
| preg_match_all | 較慢 | 無 | 極度受限環境、教學示範 |
strlen() 回傳位元組數,mb_strlen() 才是字元數PHP 7.4+ 可考慮 mbstring 配合 grapheme_substr()(需 intl 擴展)處理書寫字元簇,支援 Emoji、組合字元等複雜情況。