PHP中文截取,UTF8和GBK详解 cn_substr_gbk cn_substr_utf8

本文介绍了一种在UTF-8和GBK编码下高效截取中文字符的方法,并提供了具体的PHP实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
这个是51js上面的文章,替换dedecms的 cn_substr 听说速度更快 ,不过代码 已经通过我自己的测试

$cfg_soft_lang ='utf-8';

/**
 * 中英文截取字符串,汉字安2个字节
 *
 * @access public
 * @param string $str 需要截取的字符串
 * @param int $cutLen 截取的长度
 * @param bool $cutSlashes 是否去掉\
 * @param bool $addSlashes 是加\
 * @param string $oDot 截取后加的字符串,如经常用的三个点
 * @param bool $hasHtml 是否有html
 * @return string
 */

    function cn_substr($str, $cutLen, $oDot = null, $hasHtml = false, $cutSlashes = false, $addSlashes = false)
    {
        global $cfg_soft_lang;
        $str = trim($str);
        if ($cutSlashes) $str = stripslashes($str);
        if ($hasHtml) {
            $str = preg_replace('/(\<[^\<]*\>|\r|\n|\s|\[.+?\])/is','', $str);
            $str = htmlspecialchars($str);
        } else {
            $str = htmlspecialchars($str);
        }
        if ($cutLen && strlen($str) > $cutLen) {
            $nStr ='';
             if ($cfg_soft_lang =='utf-8'){
                $n = 0;
                $tn = 0;
                $noc = 0;
                while ($n < strlen($str)) {
                    $t = ord($str [$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 >= $cutLen) break;
                }
                if ($noc > $cutLen) $n -= $tn;
                $nStr = substr($str, 0, $n);
            }else{
                for ($i = 0; $i < $cutLen - 1; $i++) {
                    if (ord($str [$i]) > 127) {
                        $nStr .= $str [$i] . $str [$i + 1];
                        $i++;
                    } else {
                        $nStr .= $str [$i];
                    }
                }
            }
        $str = $nStr . $oDot;
}
        if ($addSlashes) $str = addslashes($str);
        $str = htmlspecialchars_decode($str);
        return trim($str);
    }





ecshop 的cn_substr

/**
* 截取UTF-8编码下字符串的函数
*
* @param string $str 被截取的字符串
* @param int $length 截取的长度
* @param bool $append 是否附加省略号
*
* @return string
*/
function sub_str($str, $length = 0, $append = true)
{
$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, EC_CHARSET);
}
elseif (function_exists('iconv_substr'))
{
$newstr = iconv_substr($str, 0, $length, EC_CHARSET);
}
else
{
//$newstr = trim_right(substr($str, 0, $length));
$newstr = substr($str, 0, $length);
}

if ($append && $str != $newstr)
{
$newstr .= '...';
}

return $newstr;
}





===========================
下面的转载的文章
2009-10-30 14:56  5356人阅读  评论(2)  收藏  举报
 

 中文字符截取是一个十分有用的功能,在很多地方都会用到,比如提取定长标题,抽取标签等

由于各种字符集的存储方式的不一样,存在双字节(GBK)多字节(Unicode)的存储方式,这就导致了统一处理的困难。

国际标准UTF8字符编码中,存储中文字符要3个字节,即把php文件存储为UTF8编码格式可以得到 strlen("中")=3

假如直接用php的字符串函数去处理类似这个字符将可能产生乱友的情况,主要是由于在一个完整字符中间强制截断产生半字符的情况。要避免这种情况的唯一办法就是把截断位置延长到这个字符结束的位置进行截断

 

 

在PHP中,提供了mbstring模块功能,mbstring以字符作为操作的基本单元,如mb_strlen("中","utf-8")=1 。 大部分的mbstring都要求输入编码,当然可以在php环境中设置默认的编码。参数设置为mbstring.internal_encoding = UTF-8, mbstring.http_input = UTF-8, mbstring.http_output = UTF-8, mbstring.substitute_character = UTF-8。由于php系统一般是存储字节来计算长度的,为了消除mbstring和string之间的差异,使用mbstring的时候就要格外小心 了。

 

假如不使用mbstring的功能 的话,可以自行编写中文字符处理的函数。

php中文截取函数,经网上搜索学习发现,大部分网络中都是以从开始位置一个一个完整字符来实现的。这有可能导致效率十分底。下面我写了个截取函数,是针对UTF8的

[php]  view plain copy
  1. <?php  
  2. $str = "d中-在f在你是d我的国家困g在有和,人。工";  
  3. var_dump(strcut($str,0,9));  
  4. function strcut($str,$start,$len){  
  5.     if($start < 0)  
  6.         $start = strlen($str)+$start;  
  7.       
  8.     $retstart = $start+getOfFirstIndex($str,$start);  
  9.     echo $retstart;  
  10.     $retend = $start + $len -1 + getOfFirstIndex($str,$start + $len);  
  11.     echo $retend;  
  12.     return substr($str,$retstart,$retend-$retstart+1);  
  13. }  
  14. //判断字符开始的位置  
  15. function getOfFirstIndex($str,$start){  
  16.     $char_aci = ord(substr($str,$start-1,1));  
  17.     if(223<$char_aci && $char_aci<240)  
  18.         return -1;  
  19.     $char_aci = ord(substr($str,$start-2,1));  
  20.     if(223<$char_aci && $char_aci<240)  
  21.         return -2;  
  22.     return 0;  
  23. }  
  24. ?>  

224-239是UTF8中文字符高位(第一位,共三位)ascii区间,换算成十六进制为0xE0 - 0xEF 二进制为1100 0000 - 1100 FFFF。通过适当偏移截取位置来避免在一个字符的中间截断的现象。

附UTF8字符编码规则:区间无重复。

 

存储字节数字节流( 二进制)(x为可变位)最高位区间(10进制)低位区间
一字节0xxxxxxx00 - 7F
二字节110xxxxx 10xxxxxxC0 - DF80 - BF
三字节1110xxxx 10xxxxxx 10xxxxxxE0 - EF80 - BF
四字节11110xxx 10xxxxxx 10xxxxxx 10xxxxxxF0 - FF80 - BF

 

 

在GBK字符编码中,存储中文字符要2个字节,即 把php文件存储为ANSI(GBK)编码格式 可以得到 strlen("中")=2

在GBK字符编码中,中文字符最高位指定是1。

GBK 亦采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE之间,剔除 xx7F一条线。总计 23940个码位,共收入 21886个汉字和图形符号,其中汉字(包括部首和构件) 21003个,图形符号 883个。高位和低位有可能相同所以无法判断高低位了。

我们知道,gbk2312编码是 在区位码的区号和位号上分别加上0xA0就得到的,区位码中01-09区是符号、数字区,16-87区是汉字区,10-15和88-94是未定义的空白区。所有汉字的编码为16-87加上0xa0,那么我们可以通过判断大于oxa1来判定是否是一个汉字字符。

php实现如下

[php]  view plain copy
  1. <?php  
  2. $str = "d中-在f在你是d我的国家困g在有和,人。工";  
  3. echo strcut($str,0,9);  
  4. function strcut($str,$start,$len){  
  5.     if($start < 0)  
  6.         $start = strlen($str)+$start;  
  7.       
  8.     $retstart = $start+getOfFirstIndex($str,$start);  
  9.     echo $retstart;  
  10.     $retend = $start + $len -1 + getOfFirstIndex($str,$start + $len);  
  11.     echo $retend;  
  12.     return substr($str,$retstart,$retend-$retstart+1);  
  13. }  
  14. //判断字符开始的位置支持GBK  
  15. function getOfFirstIndex($str,$start){  
  16.     $p = "[".chr(0xa1)."-".chr(0xff)."]+$";  
  17.     preg_match("/$p/",substr($str,$start),$res);  
  18.     if (isset($res[0]) and fmod(strlen($res[0]),2) == 1)  
  19.         return -1;  
  20.     return 0;  
  21. }  
  22. ?>  
<think>好的,用户问的是C++的std::string默认是否存储UTF-8。我需要先确认这个问题的背景。用户可能之前在处理中文字符串时遇到了问题,或者听说UTF-8是推荐的编码,所以想知道std::string是否默认使用它。 首先,我得回忆C++标准对std::string的规定。std::string实际上是basic_string<char>,而char的编码取决于编译器系统环境。默认情况下,std::string并不强制使用UTF-8,它只是存储字节序列,具体编码由源文件编码执行环境决定。 用户可能混淆了字符串的存储方式与编码方式。比如,如果源码保存为UTF-8,并且编译器正确处理,那么字符串字面量可能以UTF-8存储。但这是编译器特定的行为,不是C++标准的要求。 接下来要考虑不同平台的情况。比如,在Windows上,默认编码可能是GBK或别的,而Linux通常是UTF-8。因此,std::string在不同平台下的表现可能不同,导致跨平台问题。 用户可能需要明确如何确保std::string使用UTF-8。这时候需要提到C++11引入的u8前缀,这样字符串字面量会被明确编码为UTF-8。同时,编译器选项如-finput-charset-fexec-charset在GCC中的作用也很重要。 还要注意常见的误区,比如认为std::string知道编码,但实际上它只是字节容器。操作如长度计算或子字符串处理需要特别处理,尤其是多字节字符的情况。 用户可能遇到乱码问题,需要检查源码编码、编译器设置终端环境是否一致。比如,在Windows下输出UTF-8可能需要设置控制台代码页为65001,并且使用支持UTF-8的字体。 最后,建议用户使用明确的方法处理UTF-8,比如使用u8前缀,处理时使用专门的库如ICU或Boost.Locale,以提高可移植性正确性。同时,强调跨平台开发时明确指定编码的重要性,避免依赖默认设置。 总结回答时需要分点说明std::string的默认行为,影响编码的因素,如何正确使用UTF-8,以及常见问题解决方案。确保用户理解std::string本身不管理编码,正确使用需要开发者主动控制各个阶段。</think>在C++中,`std::string`默认并不直接等同于UTF-8编码字符串,其具体编码取决于编译环境源码保存格式。以下是关键要点分析: --- ### 一、`std::string`的本质特性 1. **底层实现**: - `std::string`是`std::basic_string<char>`的别名 - 实际存储的是`char`类型的字节序列,**无内置编码信息** 2. **编码依赖**: - 存储内容取决于: - 源码文件的保存编码(UTF-8/GBK等) - 编译器的字符集处理方式 - 执行环境的默认编码 --- ### 二、编码行为详解 #### 情形1:默认编译环境 | 平台 | 典型默认编码 | `std::string`实际内容 | |------------|--------------------|-----------------------------| | Windows | 本地编码(如GBK) | 按源码编码转换为本地ANSI编码 | | Linux/macOS| UTF-8 | 通常直接存储UTF-8字节序列 | #### 情形2:显式控制(C++11起) ```cpp // 明确指定UTF-8编码 std::string utf8_str = u8"你好世界"; // u8前缀强制UTF-8编码 ``` - 此时字符串内容保证为UTF-8字节序列 - 需要编译器支持(GCC/Clang/MSVC均支持) --- ### 三、验证实验 ```cpp #include <iostream> #include <string> int main() { std::string str = "你好"; std::cout << "字节数: " << str.size() << std::endl; // 输出取决于编码 } ``` - **UTF-8编码时**:输出`6`(每个汉字3字节) - **GBK编码时**:输出`4`(每个汉字2字节) --- ### 四、强制UTF-8的方案 1. **源码保存为UTF-8**: - 所有现代IDE/编辑器均支持 - Windows需注意BOM头(建议使用UTF-8 without BOM) 2. **编译器选项**: ```bash g++ -finput-charset=UTF-8 -fexec-charset=UTF-8 ``` - `-finput-charset`:指定源码编码 - `-fexec-charset`:指定执行字符集 3. **运行时验证**: ```cpp // 检查是否包含有效UTF-8序列 bool is_utf8(const std::string& str) { // 实现UTF-8有效性验证算法 } ``` --- ### 五、注意事项 1. **跨平台陷阱**: - Windows控制台默认使用本地编码(需`chcp 65001`切换UTF-8模式) ```cpp // Windows专用输出设置 #include <windows.h> SetConsoleOutputCP(65001); // 设置控制台为UTF-8 ``` 2. **字符串操作**: - `length()`返回的是字节数而非字符数 - 截取子串时可能破坏多字节字符 ```cpp // 安全截取UTF-8字符串 std::string utf8_substr(const std::string& str, size_t start, size_t len); ``` --- ### 六、推荐实践 1. **统一使用UTF-8**: - 源码文件UTF-8 without BOM - 编译选项添加字符集参数 - 运行时环境显式设置编码 2. **使用辅助库**: - 文本处理:`ICU`、`Boost.Locale` - 序列化:`nlohmann/json`(自动处理UTF-8) ```cpp #include <boost/locale.hpp> std::string narrow = boost::locale::conv::utf_to_utf<char>(wide_str); ``` 3. **现代C++特性**: ```cpp // C++20起新增UTF-8字面量 using namespace std::literals; auto str = "你好"sv; // string_view直接操作UTF-8 ``` --- 掌握这些原理后,虽然`std::string`默认不保证使用UTF-8,但通过正确的工程配置编码规范,完全可以将其作为UTF-8字符串容器使用。建议在项目初期就确立统一的编码策略。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值