function csubstr($text, $start=0, $limit=12) {
if (function_exists('mb_substr')) {
$more = (mb_strlen($text) > $limit) ? TRUE : FALSE;
$text = mb_substr($text, 0, $limit, 'UTF-8');
return array($text, $more);
} elseif (function_exists('iconv_substr')) {
$more = (iconv_strlen($text) > $limit) ? TRUE : FALSE;
$text = iconv_substr($text, 0, $limit, 'UTF-8');
return array($text, $more);
} else {
preg_match_all("/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/", $text, $ar);
if(func_num_args() >= 3) {
if (count($ar[0])>$limit) {
$more = TRUE;
$text = join("",array_slice($ar[0],0,$limit))."...";
} else {
$more = FALSE;
$text = join("",array_slice($ar[0],0,$limit));
}
} else {
$more = FALSE;
$text = join("",array_slice($ar[0],0));
}
return array($text, $more);
}
}
csubstr()的功能就是截节生成缩略文本字串的功能。单纯的我,以很久以前,一直以为不需要csubstr()直接用php自带的substr()就行了。可以,真的可以,只不过十分不好看。因为在中英混排时,奇数英文个数的单词或者其它半角等特殊情况会使中文单字被截断,这样就会出现一个滑稽的"?"号.这是一个无法正常显示的ascII码。经常也会出现点小问题。那就郁闷了..
似乎很早以前,有位达人,给了我一个简单的csubstr()例子,是通过逐字进行遍例,统计字串中半角、英文、其它可能出现的单字节的字符个数奇偶情况来作最后缩略文的limit值。这个方法,我现在想起来还心寒。。要是缩略文不用于标题,而是用于半文缩略,那是多大的工程啊!?
看到sablog的csubstr()后,似乎好像是用正则进行替换的。这种方法直接对limit最后一个字符进行处理。效率,想想都知道。没得说了,最优啦。所以开始对正则进行了研究。。
当正则学习到一定程度后。我开始回头看sablog的csubstr函数.顺路作了一下小研究(关于编码)。
代码如下:
<?php这段代码在UTF8与GB2312下有两种结果。
$str='我';
echo '$str:'.$str.'<br />';
echo 'String length: '.strlen($str).'<br />';
echo 'ord($str):'.ord($str).'<br />';
$sstr[1]=substr($str,0,1);
$ostr[1]=ord($sstr[1]);
echo 'echo $str(0,1):'.$sstr[1].' Ord:'.$ostr[1].' DecHex:'.dechex($ostr[1]).'<br />';
$sstr[2]=substr($str,1,1);
$ostr[2]=ord($sstr[2]);
echo 'echo $str(1,1):'.$sstr[2].' Ord:'.$ostr[2].'<br />';
$sstr[3]=substr($str,2,1);
$ostr[3]=ord($sstr[3]);
echo 'echo $str(0,1):'.$sstr[3].' Ord:'.$ostr[3].'<br />';
$sstr[0]=chr($ostr[1]).chr($ostr[2]).chr($ostr[3]);
echo 'Join Ord(1-3):'.$sstr[0].'<br />';
?>
在UTF8下php脚本的结果如下:
$str:我在GB2312下php脚本的结果如下:
String length: 3
ord($str):230
echo $str(0,1):� Ord:230 DecHex:e6
echo $str(1,1):� Ord:136
echo $str(0,1):� Ord:145
Join Ord(1-3):我
$str:我关于正则部份的分析总结:
String length: 2
ord($str):206
echo $str(0,1):� Ord:206 DecHex:ce
echo $str(1,1):� Ord:210
echo $str(0,1): Ord:0
Join Ord(1-3):我
\xhh 在php manual里的说明:
在“\x”之后最多再读取两个十六进制数字(其中的字母可以是大写或小写)。在 UTF-8 模式下,允许用“\x{...}”,花括号中的内容是表示十六进制数字的字符串。原来的十六进制转义序列 \xhh 如果其值大于 127 的话则匹配了一个双字节 UTF-8 字符。
解读:
\x01-\x7f 这是一个十六进制的字符类,换算为十进制的数值解读:
- (\x01-\x7f) = (1-127) *GB2312的非汉字ASCII码
- (\xc2-\xdf) = (194-223) *未知
- (\x80-\xbf) = (128-191) *未知
- (\xe0)=(224) *未知
- (\xa0-\xbf) = (160-191) *未知
- (\xe1-\xef) = (225-239) *未知
- (\xf0) = (240) *未知
- (\x90) = (144) *未知
- (\xf1-\xf7) = (241-247) *未知
我知道未知部份有一部份是ascii码,但不知道为什么要分这么细。。
下面这部分是我的个人推理。。未证实的:
- [\x01-\x7f],匹配英文大小写与一些符号.
- [\xc2-\xdf][\x80-\xbf],匹配GB2312的ASCII编码,length=2
- \xe0[\xa0-\xbf][\x80-\xbf]~[\xe1-\xef][\x80-\xbf][\x80-\xbf],匹配UTF8的ASCII编码范围,length=3
- \xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf],没见过四连的ASCII编码值。。不知道这个是什么。不过估计是汉字中文.难道是UTF-16?
终于我自已写了一个小小的中文(汉字)截取函数的方法:
- 只争对GB2312编码。
- 只争对短标题控制。
$str='中华a人民a共和国';
$cstr=substr($str,0,10);
preg_match_all("/[^\x7e-\xFF]/",$cstr,$preg);
$azt=count($preg[0]); //非汉字ascII码个数统计
$slen=strlen($cstr); //截取的标题长度
$lstr=ord($cstr{$slen-1}); //最后一个字符的ASCII码的ORD值
$limit=10; //节取长度
//最后的字符大于127则..
if( $lstr > chr(127)) {
//如果非汉字类为奇数,长度+1
if( $azt%2===1 ){
$cstr=substr($str,0,$limit+1).'(1)';
//如果非汉字类为偶数,长度不变
}else {
$cstr=substr($str,0,$limit).'(0)';
}
}

