php没有原生unicode支持,若没有mb(Multibyte String) 扩展,需要自己写截取中文字符串的代码。保证无乱码的关键在于保证截取的字节数正确,而这个参考编码规则即可。

如 utf8的编码规则:

Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
110xxxxx 意味着 >= 2^7 + 2^6 即 ASCLL值 >= 192 时为两个字节
1110xxxx 意味着 >= 2^7 + 2^6 + 2 ^ 5 即 ASCLL值 >= 224 时为三个字节,以此类推。

按上面的编码规则,可以先取一个字节,通过ASCLL值来判断应该截取字节的字节数。算法如下:

初始化相关数据
while(已经截取的长度 < 要截取的长度) {
临时字符 = 取当前位置后面的1个字节
根据临时字符的ASCLL值判断本次截取的字节长度
最终的字符串 += 从当前位置截取指定长度的字节
当前位置 += 本次截取的字节长度
已经截取的长度 += 本次截取的字节长度
}

返回 最终的字符串

于是有了下面这段经典的代码:

function SubUtf8String($String,$Length) {
    $pos = 0;
    $lenCutted = 0;
    $stringCutted = "";
    $strlen = strlen($String);
    $Length = min($strlen, $Length);

    while ($lenCutted > $Length){
        $StringTMP = substr($String,$pos,1);

        $ascllvalue = ord($StringTMP);

        if ( $ascllvalue >= 224 ){
            $StringTMP = substr($String,$pos,3);
            $pos = $pos + 3;
            $lenCutted = $lenCutted + 3;
        } elseif ( $ascllvalue >= 192 ){
            $StringTMP = substr($String,$pos,2);
            $pos = $pos + 2;
            $lenCutted = $lenCutted + 2;
        } else {
            $pos = $pos + 1;
            $lenCutted = $lenCutted + 1;
        }
        $stringCutted .= $StringTMP;
    }

    if ($stringCutted){
        $stringCutted .= "...";
    }
    return $stringCutted;
}

gbk编码规则:
00-7F 单字节情形
81-FE 40-FE 双字节情形
gb 18030的扩展部分
81-FE 30-39 81-FE 30-39 四字节情形
0×81308130到0xFE39FE39。四字节字符的第一个字节的编码为0×81至0xFE;第二个字节的编码范围为0×30至0×39;第三个字节编码范围为0×81至0xFE;第四个字节编码范围为0×30至0×39

根据上述规则,写出截取gbk编码的函数应该较为简单了。

 

参考资料:

 

Tagged with:  

java字符集小记

On 七月 28, 2011, in 技术记录, by pensz

该blog整理自我的笔记,可能比较乱。

1 String.getBytes(String charset) 方法:是将string 以 charset来编码,同一个string 可以以很多不同的charset来编码,得到的Byte数组也是不一样的。

2 new String (byte[], String charset) 这个是将byte数组里面的数据以charset来解码,这里很容易出现乱码,原因就是charset选择不对。
3 为什么会存在 以下代码转码的情形:

String utf8 = new String (a.getBytes(“8859_1″), “gb2312″);

原因是:首先 a 从人类角度来看是乱码了; 再次,a之前是以8859_1来编码的,后来发现真实的编码应该是 gb2312。其中的 a.getBytes(“8859_1″)只是为了获得最初的byte[],如果你有最初的byte[],完全不需要通过a.getBytes(“8859_1″)来获得。

String a = new String (bo.toByteArray(), “8859_1″);

String correct = new String (a.getBytes(“8859_1″), “gb2312″);

上面这段代码等于

String correct = new String (bo.toByteArray(), ”gb2312″);

4 java 里面的string是没有charset一说的,都是以unicode来保存的。

5 关于servlet,jsp里面的乱码问题
5.1 request.setCharacterEncoding(String) 是对request 的 body的解码有用,故设置这个对解决GET参数里面的乱码没有用;l另外需要注意浏览器发送请求的原始编码是否正确
5.2 response.setCharacterEncoding(String) 声明一下编码即可保证输出正确的编码;需要注意的是:必须要在getWriter前设置。详情可以参考 javadoc:
This method can be called repeatedly to change the character encoding. This method has no effect if it is called after getWriter has been called or after the response has been committed.
6 如何检测编码?
检测编码就是计算这一组字节在各个字符集上解码的可读性,选择可读性最高的那个编码为最终的编码,所以这件事情也不是那么简单的。
当然,utf8 bom时会有一些特殊byte可以判断。
对于html来说,简单的方法是看http header和html中声明的编码。
Tagged with: