如何利用PHP来截取一段中文字符串而不出现乱码

发表于:2007-05-25来源:作者:点击数: 标签:php截取一段字符串中文
标题:如何利用 PHP 来截取一段中文字符串而不出现乱码 作者:jeffwu(327wu@263.net) [code:1:e818e36f81] /* 功能:截取全角和半角混合的字符串以避免乱码 参数: $str_cut需要截断的字符串 $length允许字符串显示的最大长度 */ functionsubstr_cut$str_cut,

标题:如何利用PHP来截取一段中文字符串而不出现乱码

作者:jeffwu(327wu@263.net)
[code:1:e818e36f81]
/*
功能:截取全角和半角混合的字符串以避免乱码
参数:
$str_cut 需要截断的字符串
$length  允许字符串显示的最大长度

*/

function substr_cut($str_cut,$length = 30){ 

if (strlen($str_cut) > $length){
  for($i=0; $i < $length; $i++)
   if (ord($str_cut[$i]) > 128) $i++;
  $str_cut = substr($str_cut,0,$i) . "...";
}
return $str_cut;
}

[/code:1:e818e36f81]
说明:
程序的关键语句是:
[code:1:e818e36f81]
for($i=0; $i < $length; $i++)
if (ord($str_cut[$i]) > 128) $i++;

$str_cut = substr($str_cut,0,$i) . "...";
[/code:1:e818e36f81]
如果字符的ASCII码大于128,说明当前字符和下一个字符是属于一个汉字的。
则,$i++ 跳过对下一个字符的判断。
再结合循环中的 $i++ ,实际上,当遇到一个汉字时,$i 就会加 2 ,从而正确的跳过汉字。
最终实现的效果是,$i 变量指向的要么是半角的字符,要么是全角汉字的首字符,不会指向
全角汉字的第二个字符,所以,当$i >= $length 时,循环结束,使用
$str_cut = substr($str_cut,0,$i) . "..."; 截取字符时自然也就不会出现乱码了。


本人在写一个程序时需要利用PHP从一段字符串中截取指定长度的一段字符下来。以前在写ASP的时候,参考动网的程序写过类似的程序,不过,还没用PHP写过。

想偷懒,看有不有现成的代码可以用。于是,在GOOGLE中输入:PHP 截断字符 后查找到一段代码。

全文:http://www.yesky.com/SoftChannel/72342371945349120/20020510/1610570_3.shtml

引用:
#########################################################################
  如何分别全角和半角以避免乱码? 

  我们可以写这样一个函数来实现: 
[code:1:e818e36f81]
function ChgTitle($title) 

$length = 46; //我们允许字符串显示的最大长度
if (strlen($title)>$length) { 
$temp = 0; 
for($i=0; $i<$length; $i++) 
if (ord($title[$i]) > 128) $temp++; 
if ($temp%2 == 0) 
$title = substr($title,0,$length)."..."; 
else 
$title = substr($title,0,$length+1)."..."; 

return $title; 
}  
[/code:1:e818e36f81]
  这个函数原理就是截断一个字符,看看其ascII码是不是大于128,如果是,说明截断的是一个全角汉字,那么就退后一个截断。用$length控制长度 

  备注:循环判断字符串里面的 >128 的字符个数,如果半角字符为偶数,则表示位置刚好为整个汉字,如果为奇数,则为半个汉字,需要取下一个字符

#######################################################################

消化、测试这段代码后发现有问题。经过反复调试,查找出,这段代码基于的原理不正确。
它认为,汉字的两个字节其ASCII码都会大于128,其实不然,一个汉字的首个字节的ASCII码
必定是大于128的,但是第二个字节的ASCII码不一定大于128,例如:"祐",其两个字节的ASCII
码分别为:181 和 118。

经过仔细分析,最终,我利用上面的函数实现了对全角和半角字符串的截取。

一点点体会,写出来也是想和大家一起交流、学习。我不知道是不是有人已经写过上面的函数,不过,
我还没能读到,也就只能自己琢磨了,呵呵。

欢迎有兴趣的朋友来信交流。

 longnetpro 回复于:2003-11-26 02:26:16
要想搞得更精确一点的话,请看GB2312的编码规则

gb2313是这样的,第一字节范围是0x80 - 0xFE;第二字节范围是 0x40 - 0xFE,但不能为0x7F。

还可以用正则表达式,假如你想得到前三十个汉字(不是字节),可用
/^([\x00-\x7F]|[\x80-\xFE][\x40-\x7E\x80-\xFE]){30}/

我用的方法一贯是:将多字节码(如中文)转换为UNICODE,长度自然就出来了。不过一般只针对长度比较短的字符串,因为在一般的PHP编程中很少需要分析长的多字节编码的字符串(如求长度或是截取其中一部分),而且就算是不转换成UNICODE循环也很慢,不值得推荐,肯定需要用到扩展模块的(如mbstring多字节码字符串模块)。而用UNICODE转换是比较通用的办法,现在WINDOWS NT系列的操作系统都是用UNICODE作底层统一编码的。

 netkiller 回复于:2003-11-26 09:01:02
哈哈..楼主例只,只能用于GB2312,如果是GB18030,GBK不知可以吗????

[quote:bdf0b87f3e="longnetpro"]E\x80-\xFE]){30}/

我用的方法一贯是:将多字节码(如中文)转换为UNICODE,长度自然就出来了。不过一般只针对长度比较短的字符串,因为在一般的PHP编程中很少需要分析长的多字节编码的字符串(如求长度或是截取?.........[/quote:bdf0b87f3e]

我的整个站点都是UTF-8的..数据库使用UNICODE 数据存储UTF-8

member=> select substring('数据库的编码是用系统表' from 1 for 4);

 substring

-----------

 数据库的

(1 row)

 

member=> select substring('数据库的编码是用系统表' from 1 for 2);

 substring

-----------

 数据

(1 row)

 

member=> select * from 组;

序号 |     组名     |         描述

------+--------------+----------------------

    1 | 域用户       | 9812.net域内用户

    3 | 计算机维护组 | 维护计算机的用户用户

(2 rows)

 

member=> select 组名,substring(描述 from 1 for 5) as 描述 from 组;

     组名     |    描述

--------------+------------

 域用户       | 9812.

 计算机维护组 | 维护计算机

(2 rows)

 longnetpro 回复于:2003-11-26 11:20:54
你这个是对特殊设置的数据库可行,比较通用的办法还是用PHP即时转换吧。

我前一个月写了一个汉字编码转换的程序,支持UTF8,UTF16BE、LE,UTF32BE、LE,GB18030,BIG5的互转,支持繁简互转,中间代码用UNICODE。
演示地址:
简体
http://members.lycos.co.uk/longnetpro/encoding/
繁体
http://members.lycos.co.uk/longnetpro/encoding/?lang=cht

因为是演示版,并顾及到服务器安全性,因此我限制了它的很多功能,转换也只限于UTF8,GB18030,BIG5,但该类本身的功能是完整的。

注意,如果是文件转换,只能转换TXT文件。

 netkiller 回复于:2003-11-26 17:06:51
[quote:3ae2976a31="longnetpro"]你这个是对特殊设置的数据库可行,比较通用的办法还是用PHP即时转换吧。

我前一个月写了一个汉字编码转换的程序,支持UTF8,UTF16BE、LE,UTF32BE、LE,GB18030,BIG5的互转,支持繁简互转,中间代码用UNICODE。
..........[/quote:3ae2976a31]

不支持UNICODE.的数据就是不合格产品..

你那么转来转去.烦不烦...影响性能...
如果要转码.使用iconv 函数就行了.编译时加--with-iconv
数据本身也支持
select convert(描述,'UNICODE','GBK')as desc from 组;
select convert(组名 using utf_8_to_gb18030) from 组;

http://home.9812.net/linux/article/postgres/postgresql.htm

请看看..
11.8.2 PHP

 longnetpro 回复于:2003-11-26 20:35:36
[quote:87a95e989e="netkiller"]

不支持UNICODE.的数据就是不合格产品..

你那么转来转去.烦不烦...影响性能...
如果要转码.使用iconv 函数就行了.编译时加--with-iconv
数据本身也支持
select convert(描述,'UNICODE','GBK')as desc from ?.........[/quote:87a95e989e]

不支持UNICODE的数据就是不合格产品?可能你这么认为,可很多老外并不这么认为的。其实说白了,UNICODE还是为了照顾非拉丁文字语言而搞的,它为什么一定要支持?因为UNICODE只是一个组织定的标准,并不一定被所有人认同,其实现在还有争议。还有一个原因是UNICODE在很多情况下也并非绝对完美的方案。

你嫌烦就算了,又没有要你用!性能和效率问题我已尽量考虑了。再说了,你要有所得必定有所失。

任何一种方法都有各自的优缺点的,什么事也不都是完全按你想象那样来的,不是所有的人的需求和条件都和你一样的。意外的情况也很多,但是只要你用得得法,用的场合得当,没有什么不好的。不然的话,要是都明知道你的方法好,何必想方设法搞一些别的东西出来呢?一个东西的存在是有它存在的理由的,比如MS的产品,你能说它很好?但它为什么这么受欢迎?——当然你可能不喜欢它,我也不太喜欢它,但还是有很多人是喜欢它的。

所以以后发表结论的时候请客观一点,看问题的视角多一点,不是只从自己出发。

 netkiller 回复于:2003-11-27 09:08:11
[quote:1addfc0608="longnetpro"]

不支持UNICODE的数据就是不合格产品?可能你这么认为,可很多老外并不这么认为的。其实说白了,UNICODE还是为了照顾非拉丁文字语言而搞的,它为什么一定要支持?因为UNICODE只是一个组织定的标准,并不一定被所有..........[/quote:1addfc0608]

这是直接使用UNICODE与间接使用... 的问题.

UNICODE还是为了照顾非拉丁文字语言而搞的..不是为了"照顾".
而是为了跨平台兼容..

UNICODE 使你的数据库可以存储,中文(繁,简,蒙古,藏文....),日文,韩文.啊拉伯..等等.....
一个中文汉字长=假名长=韩文长=啊拉伯字长=英文.......
好处不用说...

[quote:1addfc0608]
所以以后发表结论的时候请客观一点,看问题的视角多一点,不是只从自己出发。 
[/quote:1addfc0608]

你可以看看我写的文档..
[url]http://home.9812.net/linux/article/postgres/postgresql.htm[/url]

11.8       汉字编码问题


文中提供了.4转码方案.(包括原代码) 视角还不够多吗? 目前还没想出其它方案.

 longnetpro 回复于:2003-11-27 10:04:08
从postgresql的角度来说,你的方法是很好,也的确简单易行。但并不是所有的用户都是用这种数据库的,也不是所有的用户都是用这种方式的。也不是所有的数据库都支持UNICODE的——因为现在就是存在有很多的“不合格产品”,你能怎么办?并非说你的方法不好,也并非说UNICODE不好,我也用UNICODE,我也需要转换。我所说的多视角也并非指方案有多少(你的所有方案都只是基于postgresql的),而是说不能只站在你熟悉的角度上看问题。你的好的方法和建议我会接受的。

 netkiller 回复于:2003-11-27 10:26:01
我的方法同样适合SQL99的数据.如Oracle

一般不合格数据都是SQL92的.如果他不支持UNICODE.为什么拿到其它国家销售呢..???哈哈...
:)

MS Sqlserver , Sybase 虽然是SQL92但他加了很多自己东东.跟本不是ASNI 的SQL 2 /SQL92

 znsoft 回复于:2003-11-28 10:19:29
hehe,这段代码最早是我在phpease.com发表的,来源于几年前作的汉字显示研究:)

往事不在,phpease.com也关门, 当年php红火的场面也不在了,但欣慰的是用php 的人越来越多了

上面的边界字符还有问题,最好的是判断

是否大于0xa0,这是汉字的第一个字符开始的编码

 netkiller 回复于:2003-11-28 11:44:40
哈哈.phpease.com 我和站长聊过..
我让他和其它站合作..走合并路..

原文转自:http://www.ltesting.net