我頁面上有個(gè)字符串過長需要用省略號(hào)代替的功能,現(xiàn)在我用strlen和substr實(shí)現(xiàn),發(fā)現(xiàn)中文和英文截取文字長度不一樣,導(dǎo)致中文截取的過少,英文的截取的和設(shè)置的長度的一樣。有沒有什么好的方法統(tǒng)一中文和英文一樣!表示無語啊!
認(rèn)證0級(jí)講師
在php
中編碼是UTF-8
的話占3個(gè)字節(jié);是GB2312
的話占2個(gè)字節(jié)。推薦你把所有字符設(shè)置成同一種編碼字符處理。php
除了strlen
和substr
之外,還有帶mb_
開頭的?。】梢灾付ㄗ址幋a格式例如mb_strlen和mb_substr
$len = mb_strlen($string, 'UTF-8');
$newString = $len>60?mb_substr($string, 0, 60, 'UTF-8'):$string;
試試看
讓多余的字符串顯示為省略號(hào),截取的方式是一種很落后的方式,而且字符串截取對(duì)于中文和英文截取結(jié)果不一樣。HTML5中可以直接通過css來控制:
overflow: hidden; //溢出部分影藏
white-space: nowrap; //文本不進(jìn)行換行
text-overflow: ellipsis; //當(dāng)文本溢出包含元素時(shí)顯示省略號(hào)
這三個(gè)組合使用即可。
/**
* 字符串截取方法(支持中英文,截取長度包含省略符)
* @param string $string 字符串
* @param integer $length 截取長度
* @param string $dot 省略符
* @param string $charset 編碼
* @return string
*/
function strCut($string, $length, $dot = '...', $charset = 'UTF-8') {
$charset = 'UTF-8';
$strlen = strlen($string);
if($strlen <= $length) return $string;
$string = str_replace(
array(' ',' ', '&', '"', '\'', '“', '”', '—', '<', '>', '·', '…'),
array(' ',' ', '&', '"', "'", '“', '”', '—', '<', '>', '·', '…'),
$string
);
$strcut = '';
if (strtolower($charset) == 'utf-8') {
$length = intval($length-strlen($dot)-$length/3);
$n = $tn = $noc = 0;
while ($n < strlen($string)) {
$t = ord($string[$n]);
if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
$tn = 1; $n++; $noc++;
} elseif(194 <= $t && $t <= 223) {
$tn = 2; $n += 2; $noc += 2;
} elseif(224 <= $t && $t <= 239) {
$tn = 3; $n += 3; $noc += 2;
} elseif(240 <= $t && $t <= 247) {
$tn = 4; $n += 4; $noc += 2;
} elseif(248 <= $t && $t <= 251) {
$tn = 5; $n += 5; $noc += 2;
} elseif($t == 252 || $t == 253) {
$tn = 6; $n += 6; $noc += 2;
} else {
$n++;
}
if ($noc >= $length) {
break;
}
}
if ($noc > $length) {
$n -= $tn;
}
$strcut = substr($string, 0, $n);
$strcut = str_replace(
array('∵', '&', '"', "'", '“', '”', '—', '<', '>', '·', '…'),
array(' ', '&', '"', '\'', '“', '”', '—', '<', '>', '·', '…'),
$strcut
);
} else {
$dotlen = strlen($dot);
$maxi = $length - $dotlen - 1;
$current_str = '';
$search_arr = array('&',' ', '"', "'", '“', '”', '—', '<', '>', '·', '…','∵');
$replace_arr = array('&',' ', '"', '\'', '“', '”', '—', '<', '>', '·', '…',' ');
$search_flip = array_flip($search_arr);
for ($i = 0; $i < $maxi; $i++) {
$current_str = ord($string[$i]) > 127 ? $string[$i].$string[++$i] : $string[$i];
if (in_array($current_str, $search_arr)) {
$key = $search_flip[$current_str];
$current_str = str_replace($search_arr[$key], $replace_arr[$key], $current_str);
}
$strcut .= $current_str;
}
}
return $strcut.$dot;
}
推薦使用這個(gè)方法來避免一下尷尬:
(1) substr截取中文會(huì)出現(xiàn)亂碼的情況:
$string = '中文字符中文字符';
var_dump(substr($string, 0, 10));
結(jié)果:string(10) "中文字?"
原因:中文占3個(gè)字符,substr
可能會(huì)把某個(gè)中文截取了一部分,使中文亂碼。
(2) mb_substr截取會(huì)出現(xiàn)字符太長的情況:
$string = '中文字符englishword';
var_dump(mb_substr($string, 0, 10));
結(jié)果:string(18) "中文字符englis"
原因:中文占3個(gè)字符,輸出結(jié)果的字符串實(shí)際占18個(gè)字符,并不是期望的10個(gè)字符。
使用情景:
微信支付商品名稱有128個(gè)字符限制,在UTF-8編碼下,中英文字符的總字符長度要控制在128個(gè)以內(nèi),個(gè)人覺得用這個(gè)方法比較合適。
其實(shí)題主的問題是:中文英文截取的長度一樣,這里的長度并非指字符長度(Length、Byte),而是像素寬度(Width)
此理論與UTF-8中漢字是3個(gè)字符無關(guān),其實(shí)某些生僻的漢字、Emoji占有4個(gè)字符。
UTF-8最長是6個(gè)字符長(1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)。
按照漢字(東亞字系)的字體設(shè)計(jì),一般情況下 1個(gè)漢字 ≈ 2個(gè)英文(數(shù)字、符號(hào)等) 的 像素寬度
如:
1a 12ab 123abc 1234abcd
漢 漢字 漢字漢 漢字漢字
可以看到1個(gè)漢字 ≈ 2個(gè)英文的width
在早期的網(wǎng)站中,一般使用SimSun(宋體),在SimSun的設(shè)計(jì)中,英文的寬度 == 1/2的漢字。
隨著互聯(lián)網(wǎng)的發(fā)展,一般的字體已經(jīng)不能滿足大家的需要,所以字體百花齊放的今天,只能 ≈ (約等于)
比如segmentfault的字符方案中,英文多出了1個(gè)寬度,但是不影響整體效果
實(shí)現(xiàn)
前端:(這是最好的方案)
overflow: hidden; //此句必須
white-space: nowrap; //對(duì)于無需換行的場(chǎng)景,可以設(shè)置width/height為固定值
text-overflow: ellipsis; //此行必須,但是Firefox部分版本不兼容
后端: mb_substr,cutStr(能署名代碼來自于Discuz!可以嗎?)的答案,都是錯(cuò)誤的,這些得到的結(jié)果漢字寬度大于英文的寬度
請(qǐng)查看以下代碼:以UTF-8為例子
/**
* 移除字符串的BOM
*
* @param string $str 輸入字符串
* @return string 輸出字符串
*/
function removeBOM($str)
{
$str_3 = substr($str, 0, 3);
if ($str_3 == pack('CCC',0xef,0xbb,0xbf)) //utf-8
return substr($str, 3);
return $str;
}
/**
* 按UTF-8分隔為數(shù)組,效率比MB_Substr高
* 0xxxxxxx
* 110xxxxx 10xxxxxx
* 1110xxxx 10xxxxxx 10xxxxxx
* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*
* @param string $str 輸入utf-8字符串
* @return array 返回成一段數(shù)組
*/
function str_split_utf8($str)
{
return preg_match_all('/./u', removeBOM($str), $out) ? $out[0] : FALSE;
}
/**
* 按非ascii字符占有幾個(gè)字寬的方式切分字符串,并且不會(huì)將漢字切成半個(gè)
* 所謂字寬是指,使用默認(rèn)字體顯示時(shí),非ascii字符相比英文字符所占大小,比如:宋體、微軟雅黑中,漢字占兩個(gè)寬度
* @example $ansi_width = 2 表示漢字等非英文字符按照兩個(gè)字寬長度
* @example $ansi_width = 1 表示所有字符按一個(gè)字寬長度
*
* @param string $string 原始字符
* @param integer $offset 開始偏移,使用方法和substr一樣,可以為負(fù)數(shù)
* @param integer $length 長度,使用方法和substr一樣,可以為負(fù)數(shù)
* @param integer $ansi_width 漢字等非英文字符按照幾個(gè)字符來處理
* @return string 返回裁減的字符串
*/
function substr_ansi($string, $offset, $length = 0, $ansi_width = 1)
{
if (empty($string)) return $string;;
$data = str_split_utf8($string);
if (empty($data)) return $string;
$as = $_as = array();
$_start = $_end = 0;
foreach($data as $k => $v)
$as[$k] = strlen($v) > 1 ? $ansi_width : 1;
$_as_rev = array_reverse($as,true);
$_as = $offset < 0 ? $_as_rev : $as;
$n = 0; $_offset = abs($offset);
foreach($_as as $k => $v) {
if ($n >= $_offset) {
$_start = $k;
break;
}
$n += $v;
}
//echo $_start,',';
$_as = $length <= 0 ? $_as_rev : $as;
end($_as); list($_end) = each($_as); reset($_as);//給$_end 設(shè)定默認(rèn)值,一直到結(jié)尾
$n = 0; $_length = abs($length);
foreach($_as as $k => $v) {
if ($k >= $_start) {
if ($n >= $_length) {
$_end = $k + ($length <= 0 ? 1 : 0);
break;
}
$n += $v;
}
}
//echo $_end,'|||||';
if ($_end <= $_start)
return '';
$_data = array_slice($data, $_start, $_end - $_start);
return implode('',$_data);
}
/**
* 按非ascii字符占有幾個(gè)字寬的方式計(jì)算字符串長度
* @example $ansi_width = 2 表示漢字等非英文字符按照兩個(gè)字寬長度
* @example $ansi_width = 1 表示所有字符按一個(gè)字節(jié)長度
*
* @param string $string 原始字符
* @param integer $ansi_width 漢字等非英文字符按照幾個(gè)字寬來處理
* @return string 返回字符串長度
*/
function strlen_ansi($string, $ansi_width = 1)
{
if (empty($string)) return 0;
$data = str_split_utf8($string);
if (empty($data)) return 0;
$as = 0;
foreach($data as $k => $v)
$as += strlen($v) > 1 ? $ansi_width : 1;
unset($data);
return $as;
}
/**
* smarty truncate 代碼算法來自于Smarty
* @param string
* @param integer
* @param string
* @param boolean
* @param boolean
* @return string
*/
function truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false)
{
if ($length == 0)
return '';
$ansi_as = 2;
if (strlen_ansi($string, $ansi_as) > $length) {
$length -= min($length, strlen_ansi($etc, $ansi_as));
if (!$break_words && !$middle) {
$string = preg_replace('/\s+?(\S+)?$/u', '', substr_ansi($string, 0, $length+1, $ansi_as));
}
if(!$middle) {
return substr_ansi($string, 0, $length, $ansi_as) . $etc;
} else {
return substr_ansi($string, 0, $length/2, $ansi_as) . $etc . substr_ansi($string, -$length/2, 0, $ansi_as);
}
} else {
return $string;
}
}
substr_ansi
、 truncate
便是你要的截取的函數(shù)
// substr_ansi ($offset, $length, $ansi_width)
// 如果ansi_width = 2,則表示將漢字當(dāng)做2個(gè)寬度處理
// offset length 在實(shí)際截取過程中,以英文的長度為準(zhǔn)即可
echo substr_ansi('漢字我愛你', 0, 5, 2); //輸出:漢字我
echo substr_ansi('漢字abc我愛你', 0, 5, 2); //輸出:漢字a
echo substr_ansi('abcdef', 0, 5, 2); //輸出:abcde
echo mb_substr('漢字我愛你', 0, 5); //輸出:漢字我愛你
echo mb_substr('漢字abc我愛你', 0, 5); //輸出:漢字abc
echo mb_substr('abcdef', 0, 5); //輸出:abcde
可以看到上面substr_ansi的截取后的像素寬度是正確的,并且,漢字不會(huì)截取半個(gè)
下面的mb_substr長度明顯不一致
針對(duì)日文、GBK、GB2312、Unicode等情況,請(qǐng)參見:
http://www.load-page.com:8989...
由于實(shí)在沒有精力,以及答主不太懂日文(韓文),有些字符集的ASCII區(qū)域無法弄清楚,但是此代碼在中文方面經(jīng)過生產(chǎn)環(huán)境的驗(yàn)證,已經(jīng)沒有什么問題。有了解東亞,歐洲等字符集的同好,歡迎私信聯(lián)系我。
string mb_strimwidth ( string $str , int $start , int $width [, string $trimmarker = "" [, string $encoding = mb_internal_encoding() ]] )
PHP提供的這個(gè)函數(shù)看起來可以滿足你的要求. $trimmarker 是如果長度超了, 后面添加的...這三字符.
public static function mb_substr(&$str, $length, $encoding = 'utf-8')
{
return isset($str) ? mb_substr($str, 0, $length, $encoding) . ($length < mb_strlen($str, $encoding) ? '...' : '') : '';
}
https://github.com/letwang/le...