84669 personnes étudient
152542 personnes étudient
20005 personnes étudient
5487 personnes étudient
7821 personnes étudient
359900 personnes étudient
3350 personnes étudient
180660 personnes étudient
48569 personnes étudient
18603 personnes étudient
40936 personnes étudient
1549 personnes étudient
1183 personnes étudient
32909 personnes étudient
我页面上有个字符串过长需要用省略号代替的功能,现在我用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就可以了吧,我基本都是这样解决的。