我页面上有个字符串过长需要用省略号代替的功能,现在我用strlen和substr实现,发现中文和英文截取文字长度不一样,导致中文截取的过少,英文的截取的和设置的长度的一样。有没有什么好的方法统一中文和英文一样!表示无语啊!
认证0级讲师
在php中编码是UTF-8的话占3个字节;是GB2312的话占2个字节。推荐你把所有字符设置成同一种编码字符处理。php除了strlen和substr之外,还有带mb_开头的啊!可以指定字符串编码格式例如mb_strlen和mb_substr
php
UTF-8
GB2312
strlen
substr
mb_
$len = mb_strlen($string, 'UTF-8'); $newString = $len>60?mb_substr($string, 0, 60, 'UTF-8'):$string;
试试看
让多余的字符串显示为省略号,截取的方式是一种很落后的方式,而且字符串截取对于中文和英文截取结果不一样。HTML5中可以直接通过css来控制:
overflow: hidden; //溢出部分影藏 white-space: nowrap; //文本不进行换行 text-overflow: ellipsis; //当文本溢出包含元素时显示省略号
这三个组合使用即可。
/** * 字符串截取方法(支持中英文,截取长度包含省略符) * @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; }
推荐使用这个方法来避免一下尴尬:
(1) substr截取中文会出现乱码的情况:
$string = '中文字符中文字符'; var_dump(substr($string, 0, 10));
结果:string(10) "中文字�"原因:中文占3个字符,substr可能会把某个中文截取了一部分,使中文乱码。
string(10) "中文字�"
(2) mb_substr截取会出现字符太长的情况:
$string = '中文字符englishword'; var_dump(mb_substr($string, 0, 10));
结果:string(18) "中文字符englis"原因:中文占3个字符,输出结果的字符串实际占18个字符,并不是期望的10个字符。
string(18) "中文字符englis"
使用情景:微信支付商品名称有128个字符限制,在UTF-8编码下,中英文字符的总字符长度要控制在128个以内,个人觉得用这个方法比较合适。
使用情景:
其实题主的问题是:中文英文截取的长度一样,这里的长度并非指字符长度(Length、Byte),而是像素宽度(Width)
此理论与UTF-8中汉字是3个字符无关,其实某些生僻的汉字、Emoji占有4个字符。 UTF-8最长是6个字符长(1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)。
此理论与UTF-8中汉字是3个字符无关,其实某些生僻的汉字、Emoji占有4个字符。
UTF-8最长是6个字符长(1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)。
按照汉字(东亚字系)的字体设计,一般情况下 1个汉字 ≈ 2个英文(数字、符号等) 的 像素宽度如:
1a 12ab 123abc 1234abcd 汉 汉字 汉字汉 汉字汉字
可以看到1个汉字 ≈ 2个英文的width
在早期的网站中,一般使用SimSun(宋体),在SimSun的设计中,英文的宽度 == 1/2的汉字。随着互联网的发展,一般的字体已经不能满足大家的需要,所以字体百花齐放的今天,只能 ≈ (约等于)比如segmentfault的字符方案中,英文多出了1个宽度,但是不影响整体效果
实现
前端:(这是最好的方案)
overflow: hidden; //此句必须 white-space: nowrap; //对于无需换行的场景,可以设置width/height为固定值 text-overflow: ellipsis; //此行必须,但是Firefox部分版本不兼容
后端: mb_substr,cutStr(能署名代码来自于Discuz!可以吗?)的答案,都是错误的,这些得到的结果汉字宽度大于英文的宽度
请查看以下代码:以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分隔为数组,效率比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 返回成一段数组 */ function str_split_utf8($str) { return preg_match_all('/./u', removeBOM($str), $out) ? $out[0] : FALSE; } /** * 按非ascii字符占有几个字宽的方式切分字符串,并且不会将汉字切成半个 * 所谓字宽是指,使用默认字体显示时,非ascii字符相比英文字符所占大小,比如:宋体、微软雅黑中,汉字占两个宽度 * @example $ansi_width = 2 表示汉字等非英文字符按照两个字宽长度 * @example $ansi_width = 1 表示所有字符按一个字宽长度 * * @param string $string 原始字符 * @param integer $offset 开始偏移,使用方法和substr一样,可以为负数 * @param integer $length 长度,使用方法和substr一样,可以为负数 * @param integer $ansi_width 汉字等非英文字符按照几个字符来处理 * @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 设定默认值,一直到结尾 $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字符占有几个字宽的方式计算字符串长度 * @example $ansi_width = 2 表示汉字等非英文字符按照两个字宽长度 * @example $ansi_width = 1 表示所有字符按一个字节长度 * * @param string $string 原始字符 * @param integer $ansi_width 汉字等非英文字符按照几个字宽来处理 * @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便是你要的截取的函数
substr_ansi
truncate
// substr_ansi ($offset, $length, $ansi_width) // 如果ansi_width = 2,则表示将汉字当做2个宽度处理 // offset length 在实际截取过程中,以英文的长度为准即可 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的截取后的像素宽度是正确的,并且,汉字不会截取半个下面的mb_substr长度明显不一致
针对日文、GBK、GB2312、Unicode等情况,请参见:http://www.load-page.com:8989...由于实在没有精力,以及答主不太懂日文(韩文),有些字符集的ASCII区域无法弄清楚,但是此代码在中文方面经过生产环境的验证,已经没有什么问题。有了解东亚,欧洲等字符集的同好,欢迎私信联系我。
text-overflow:ellipsis
string mb_strimwidth ( string $str , int $start , int $width [, string $trimmarker = "" [, string $encoding = mb_internal_encoding() ]] )
PHP提供的这个函数看起来可以满足你的要求. $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...
baidu就可以了吧,我基本都是这样解决的。
在
php
中编码是UTF-8
的话占3个字节;是GB2312
的话占2个字节。推荐你把所有字符设置成同一种编码字符处理。php
除了strlen
和substr
之外,还有带mb_
开头的啊!可以指定字符串编码格式例如mb_strlen和mb_substr试试看
让多余的字符串显示为省略号,截取的方式是一种很落后的方式,而且字符串截取对于中文和英文截取结果不一样。HTML5中可以直接通过css来控制:
这三个组合使用即可。
推荐使用这个方法来避免一下尴尬:
(1) substr截取中文会出现乱码的情况:
结果:
string(10) "中文字�"
原因:中文占3个字符,
substr
可能会把某个中文截取了一部分,使中文乱码。(2) mb_substr截取会出现字符太长的情况:
结果:
string(18) "中文字符englis"
原因:中文占3个字符,输出结果的字符串实际占18个字符,并不是期望的10个字符。
使用情景:
微信支付商品名称有128个字符限制,在UTF-8编码下,中英文字符的总字符长度要控制在128个以内,个人觉得用这个方法比较合适。其它答案错误的理解
其实题主的问题是:中文英文截取的长度一样,这里的长度并非指字符长度(Length、Byte),而是像素宽度(Width)
按照汉字(东亚字系)的字体设计,一般情况下 1个汉字 ≈ 2个英文(数字、符号等) 的 像素宽度
如:
可以看到1个汉字 ≈ 2个英文的width
要保证截取后的字符等长
实现
前端:(这是最好的方案)
后端: mb_substr,cutStr(能署名代码来自于Discuz!可以吗?)的答案,都是错误的,这些得到的结果汉字宽度大于英文的宽度
请查看以下代码:以UTF-8为例子
substr_ansi
、truncate
便是你要的截取的函数可以看到上面substr_ansi的截取后的像素宽度是正确的,并且,汉字不会截取半个
下面的mb_substr长度明显不一致
text-overflow:ellipsis
PHP提供的这个函数看起来可以满足你的要求. $trimmarker 是如果长度超了, 后面添加的...这三字符.
https://github.com/letwang/le...
baidu就可以了吧,我基本都是这样解决的。