存档

文章标签 ‘mb_substr’

PHP根据长度使用特殊符号分割字符串

2013年11月20日 评论已被关闭

主要是chunk_split()函数的功能
与str_split()的区别是str_split()是把字符串按照长度分割成数组,而chunk_split()则是用一个指定的特殊符按照长度分割成新的字符串。
相当于 str_split()+implode()两个函数的功能。
同样这个函数是不支持中文的,需要实现这样的功能需要自定义函数。

<?php
    $data = "e0ab71ab9ed24e627a24e7d65367936393cb3b39db9a9e84d65cd7a9254a4665";
    echo chunk_split($data, 30, "<br />");
    echo implode("<br />",str_split($data,30));//和chunk_split效果一样
?>

支持中文的自定义chunk_split()函数

<?php
//from Peter from dezzignz.com 05-Apr-2010 11:30 @ php.net

function mbStringToArray ($str) {
    if (empty($str)) return false;
    $len = mb_strlen($str);
    $array = array();
    for ($i = 0; $i < $len; $i++) {
        $array[] = mb_substr($str, $i, 1);
    }
    return $array;
}

function mb_chunk_split($str, $len, $glue) {
    if (empty($str)) return false;
    $array = mbStringToArray ($str);
    $n = 0;
    $new = '';
    foreach ($array as $char) {
        if ($n < $len) $new .= $char;
        elseif ($n == $len) {
            $new .= $glue . $char;
            $n = 0;
        }
        $n++;
    }
    return $new;
}


$data = "e0ab你好4e627a24e7d65367936393cb3b39db9钓鱼岛a9e84d65cd7a9254a4665";
echo mb_chunk_split($data, 30, "<br />");
?>

分类: php 标签: ,

实现中文字串截取无乱码的方法

2013年8月5日 评论已被关闭

function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true)
    {
        if(function_exists("mb_substr"))
            return mb_substr($str, $start, $length, $charset);
        elseif(function_exists('iconv_substr')) {
            return iconv_substr($str,$start,$length,$charset);
        }
        $re['utf-8']   = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $slice = join("",array_slice($match[0], $start, $length));
        if($suffix) return $slice."…";
        return $slice;
    }

分类: php 标签:

比较discuz和ecshop的截取字符串函数php版

2012年11月23日 评论已被关闭

网上看到一篇文章 discuz和ecshop截取字符串的两个函数,比较了一下两个版本的函数,都各有局限,只能在特定的前提下使用,但是学习一下有利于拓宽思路,了解PHP的扩展功能

下面先给出两个版本函数的源代码以及简单测试,最后我会给出一个实用性更强的字符串截取函数。需要注意的是:这里讨论的字符串截取问题都是针对UTF-8编码的中文字符串。
discuz版本

复制代码 代码如下:

/**
* [discuz] 基于PHP没有安装 mb_substr 等扩展截取字符串,如果截取中文字则按2个字符计算
* @param $string 要截取的字符串
* @param $length 要截取的字符数
* @param $dot 替换截掉部分的结尾字符串
* @return 返回截取后的字符串
*/
function cutstr($string, $length, $dot = '…') {
// 如果字符串小于要截取的长度则直接返回
// 此处使用strlen获取字符串长度有很大的弊病,比如对字符串“新年快乐”要截取4个中文字符,
// 那么必须知道这4个中文字符的字节数,否则返回的字符串可能会是“新年快乐…”
if (strlen($string) <= $length) {
return $string;
}
// 转换原字符串中htmlspecialchars
$pre = chr(1);
$end = chr(1);
$string = str_replace ( array ('&', '"', '<', '>' ), array ($pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end ), $string );
$strcut = ''; // 初始化返回值
// 如果是utf-8编码(这个判断有点不全,有可能是utf8)
if (strtolower ( CHARSET ) == 'utf-8') {
// 初始连续循环指针$n,最后一个字位数$tn,截取的字符数$noc
$n = $tn = $noc = 0;
while ( $n < strlen ( $string ) ) {
$t = ord ( $string [$n] );
if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
// 如果是英语半角符号等,$n指针后移1位,$tn最后字是1位
$tn = 1;
$n++;
$noc++;
} elseif (194 <= $t && $t <= 223) {
// 如果是二字节字符$n指针后移2位,$tn最后字是2位
$tn = 2;
$n += 2;
$noc += 2;
} elseif (224 <= $t && $t <= 239) {
// 如果是三字节(可以理解为中字词),$n后移3位,$tn最后字是3位
$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;
}
}
// 这个地方是把最后一个字去掉,以备加$dot
if ($noc > $length) {
$n -= $tn;
}
$strcut = substr ( $string, 0, $n );
} else {
// 并非utf-8编码的全角就后移2位
for ($i = 0; $i < $length; $i ++) {
$strcut .= ord ( $string [$i] ) > 127 ? $string [$i] . $string [++ $i] : $string [$i];
}
}
// 再还原最初的htmlspecialchars
$strcut = str_replace( array ($pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end ), array ('&', '"', '<', '>' ), $strcut );
$pos = strrpos ( $strcut, chr ( 1 ) );
if ($pos !== false) {
$strcut = substr ( $strcut, 0, $pos );
}
return $strcut . $dot; // 最后把截取加上$dot输出
}

discuz版本的最大缺陷在于使用 strlen 获取原始字符串的长度,并用来和传入的要截取长度参数(字节数)进行比较,由于UTF-8的中文字符的字节数是不固定的,所以就会面临这样的窘境:如果要截取4个中文字符应该指定多大的截取长度呢?8字节还是12字节呢?。。。这是无法预计的,也正是因为这个问题discuz的cutstr实际是有bug的,通过下面的测试结果能看出:

复制代码 代码如下:

$str1 = "欲穷千里目";
echo my_cutstr($str1, 10, "…")."n"; // 输出:欲穷千里目… [这是一个bug,想想是什么原因导致?]
echo my_cutstr($str1, 15, "…")."n"; // 输出:欲穷千里目

导致上述bug的原因在与cutstr函数在截取字符的时候是将一个中文字按2个字符算,那么5个中文字就是10字符,而原始字符串的长度是15字节,所以cutstr认为“成功地”从15字符的串上截取了10个字符,然后加上了“尾巴”。要解决这个bug只要在判断一下返回的子串是否和原始串相同,如果相同就不加“尾巴”。
ecshop版

复制代码 代码如下:

/**
* [ecshop] 基于PHP的 mb_substr,iconv_substr 这两个扩展来截取字符串,中文字符都是按1个字符长度计算;
* 该函数仅适用于utf-8编码的中文字符串。
*
* @param $str 原始字符串
* @param $length 截取的字符数
* @param $append 替换截掉部分的结尾字符串
* @return 返回截取后的字符串
*/
function sub_str($str, $length = 0, $append = '…') {
$str = trim($str);
$strlength = strlen($str);
if ($length == 0 || $length >= $strlength) {
return $str;
} elseif ($length < 0) {
$length = $strlength + $length;
if ($length < 0) {
$length = $strlength;
}
}
if ( function_exists('mb_substr') ) {
$newstr = mb_substr($str, 0, $length, 'utf-8');
} elseif ( function_exists('iconv_substr') ) {
$newstr = iconv_substr($str, 0, $length, 'utf-8');
} else {
//$newstr = trim_right(substr($str, 0, $length));
$newstr = substr($str, 0, $length);
}
if ($append && $str != $newstr) {
$newstr .= $append;
}
return $newstr;
}

ecshop版的特点和缺点都在于将中文字符算作一个字符,如果原始字符串中不含中文,比如:abcd1234,如果本意是要截取4个中文字符或者8个英文字符,那么使用ecshop的版本就得不到期望的结果,返回值的是:abcd。下面是简单的测试结果:

复制代码 代码如下:

$str1 = "白日依山尽,黄河入海流";
echo $str1."n";
echo my_sub_str($str1, 4, "…")."n"; // 输出:白日依山…
$str2 = "白1日2依3山4";
echo $str2."n";
echo my_sub_str($str2, 4, "…")."n"; // 输出:白1日2…

优化版
截取中文字符串的大部分应用场景是“原始字符串可以是中文、英文、数字混杂的,中文字按2个字符算,英文数字按1个字符算”,针对这个需求下面给出一个实现版本:

复制代码 代码如下:

/**
* 字符串截取,中文字符按2个字符计算,同时支持GBK和UTF-8编码
* @param $string 要截取的字符串
* @param $length 要截取的字符数
* @param $append 添加到子串后的尾巴
* @return 返回截取后的字符串
*/
function substring($string, $length, $append = false) {
if ( $length <= 0 ) {
return '';
}
// 检测原始字符串是否为UTF-8编码
$is_utf8 = false;
$str1 = @iconv("UTF-8", "GBK", $string);
$str2 = @iconv("GBK", "UTF-8", $str1);
if ( $string == $str2 ) {
$is_utf8 = true;
// 如果是UTF-8编码,则使用GBK编码的
$string = $str1;
}
$newstr = '';
for ($i = 0; $i < $length; $i ++) {
$newstr .= ord ($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
}
if ( $is_utf8 ) {
$newstr = @iconv("GBK", "UTF-8", $newstr);
}
if ($append && $newstr != $string) {
$newstr .= $append;
}
return $newstr;
}

测试结果见下(GBK和UTF-8的结果一致):

复制代码 代码如下:

$str1 = "白日依山尽,黄河入海流";
echo substring($str1, 4, "…")."n"; // 输出:白日…
echo substring($str1, 5, "…")."n"; // 输出:白日依…
$str2 = "12白34日56依78山";
echo substring($str2, 4, "…")."n"; // 输出:12白…
echo substring($str2, 5, "…")."n"; // 输出:12白3…

作者:edwardlost' blog

分类: php 标签:


标签

ajax apache array array_slice CentOS checkbox composer cookie csv curl date DATE_FORMAT excel FCKEditor file_get_contents laravel Magento Certified Developer mb_substr memcache Memcached MySQL String Functions Netbeans openssl php面试题 preg_match_all preg_replace QQ登录 rewrite SaeStorage serialize session setTimeout Sphinx spl Storage strtotime ueditor vsftp vsftpd Zend zip 分割字符串 图片处理 新浪云 正则表达式

存档