在编码转错的情况下,如何恢复

在编码转错的情况下,如何恢复

编码问题的确非常的棘手。很多时候,你一不小心,在某个阶段没有正确的转码,结果造成存储下来的文件 或者 保存进入数据库的字段是乱码。如果这个字段是非常重要的信息,你可能认为,你已经无法恢复这些信息了,其实,在大多数情况下面这个是能够恢复的,你要分析你转码的每个过程,确定出差错的位置,然后进行这个编码的逆转。

 

这里我只举一个我实际中遇到的一个例子。这个例子的情况是这样的,我在cookie里面保存了一个username字段,写cookie是通过PHP程序写的,这个PHP程序的编码是gbk的。另外一个统计系统,这个统计系统,通过Javascript 读取cookie信息,然后通过url编码这些信息,发送到服务器端。这个统计的服务器的编码是utf-8的。

 

我在设计这个系统的时候,username是英文或者数字,但是运行了一段时间以后,那个PHP网站变了,允许中文字符了,但是我这边的统计系统并不知道,这样可想而知,保存进入数据库的是一堆乱码。

因为用户名是一个用户的唯一标志,这个字段要是丢失,后果非常的严重。 如何把这段乱码恢复过来呢?

首先你要分析整个过程:

1. PHP写cookie,不会进行转换编码,但是会对字符进行urlencode。

2. Javascript 读取cookie的内容,进行urldecode,保存进入一个字符串

3. 调用Javascript urlencode函数,这个函数有问题,他默认认为Javascript 的 编码是本地Unicode的。

    并转换成utf-8 。顺便提一句,比如你的页面是gbk的,但是 a = "变量值" a内部保存是Unicode的,不再是gbk。浏览器会进行编码转换。

4. 服务器端把这个字符串转码,然后保存进入数据库。

 可以发现,实际上出错的地方就是Javascript 认为cookie读到的字符串是本地Unicode的。而实际上是一个gbk编码的字符串。

 这样只要倒过来就可以了:读出数据库字符串的内容,把utf-8的代码转成Unicode,这个转过后的Unicode 就是原来的 gbk编码的序列。

 下面附上一段源代码。为了查看字符串内部字节的排列,我把序列都用urlencode过。

 

ExpandedBlockStart.gif 代码
<? php
$name   =   " 王志文 " // 著名电影明星
$urlencode_name   =   " %CD%F5%D6%BE%CE%C4 " ;

// 存在数据库中的字节序列
$db_name   =   " 脥玫脰戮脦脛 " ;
$urlencode_dnname   =   " %C3%8D%C3%B5%C3%96%C2%BE%C3%8E%C3%84 " ;

$unicode   =  utf8_to_unicode( urldecode ( $urlencode_dnname ));

$decodename   =   "" ;
foreach  ( $unicode   as   $code )
{
    
$decodename   .=   chr ( $code );
}
echo   $decodename ;

function  utf8_to_unicode( $str
{
    
$mState   =   0 ;      //  cached expected number of octets after the current octet
                     // until the beginning of the next UTF8 character sequence

     $mUcs4    =   0 ;      //  cached Unicode character
     $mBytes   =   1 ;      //  cached expected number of octets in the current sequence

    
$out   =   array ();

    
$len   =   strlen ( $str );

    
for ( $i   =   0 $i   <   $len $i ++ ) {

        
$in   =   ord ( $str { $i });

        
if  (  $mState   ==   0 ) {

            
//  When mState is zero we expect either a US-ASCII character or a
            // multi-octet sequence.

             if  ( 0   ==  ( 0x80   &  ( $in ))) {
                
//  US-ASCII, pass straight through.
                 $out []  =   $in ;
                
$mBytes   =   1 ;

            } 
else   if  ( 0xC0   ==  ( 0xE0   &  ( $in ))) {
                
//  First octet of 2 octet sequence
                 $mUcs4   =  ( $in );
                
$mUcs4   =  ( $mUcs4   &   0x1F <<   6 ;
                
$mState   =   1 ;
                
$mBytes   =   2 ;

            } 
else   if  ( 0xE0   ==  ( 0xF0   &  ( $in ))) {
                
//  First octet of 3 octet sequence
                 $mUcs4   =  ( $in );
                
$mUcs4   =  ( $mUcs4   &   0x0F <<   12 ;
                
$mState   =   2 ;
                
$mBytes   =   3 ;

            } 
else   if  ( 0xF0   ==  ( 0xF8   &  ( $in ))) {
                
//  First octet of 4 octet sequence
                 $mUcs4   =  ( $in );
                
$mUcs4   =  ( $mUcs4   &   0x07 <<   18 ;
                
$mState   =   3 ;
                
$mBytes   =   4 ;

            } 
else   if  ( 0xF8   ==  ( 0xFC   &  ( $in ))) {
                
/*  First octet of 5 octet sequence.
                *
                * This is illegal because the encoded codepoint must be either
                * (a) not the shortest form or
                * (b) outside the Unicode range of 0-0x10FFFF.
                * Rather than trying to resynchronize, we will carry on until the end
                * of the sequence and let the later error handling code catch it.
                
*/
                
$mUcs4   =  ( $in );
                
$mUcs4   =  ( $mUcs4   &   0x03 <<   24 ;
                
$mState   =   4 ;
                
$mBytes   =   5 ;

            } 
else   if  ( 0xFC   ==  ( 0xFE   &  ( $in ))) {
                
//  First octet of 6 octet sequence, see comments for 5 octet sequence.
                 $mUcs4   =  ( $in );
                
$mUcs4   =  ( $mUcs4   &   1 <<   30 ;
                
$mState   =   5 ;
                
$mBytes   =   6 ;

            } 
else  {
                
/*  Current octet is neither in the US-ASCII range nor a legal first
                 * octet of a multi-octet sequence.
                 
*/
                
trigger_error (
                        
' utf8_to_unicode: Illegal sequence identifier  ' .
                            
' in UTF-8 at byte  ' . $i ,
                        
E_USER_WARNING
                    );
                
return   FALSE ;

            }

        } 
else  {

            
//  When mState is non-zero, we expect a continuation of the multi-octet
            // sequence

             if  ( 0x80   ==  ( 0xC0   &  ( $in ))) {

                
//  Legal continuation.
                 $shift   =  ( $mState   -   1 *   6 ;
                
$tmp   =   $in ;
                
$tmp   =  ( $tmp   &   0x0000003F <<   $shift ;
                
$mUcs4   |=   $tmp ;

                
/* *
                * End of the multi-octet sequence. mUcs4 now contains the final
                * Unicode codepoint to be output
                
*/
                
if  ( 0   ==   -- $mState ) {

                    
/*
                    * Check for illegal sequences and codepoints.
                    
*/
                    
//  From Unicode 3.1, non-shortest form is illegal
                     if  ((( 2   ==   $mBytes &&  ( $mUcs4   <   0x0080 ))  ||
                        ((
3   ==   $mBytes &&  ( $mUcs4   <   0x0800 ))  ||
                        ((
4   ==   $mBytes &&  ( $mUcs4   <   0x10000 ))  ||
                        (
4   <   $mBytes ||
                        
//  From Unicode 3.2, surrogate characters are illegal
                        (( $mUcs4   &   0xFFFFF800 ==   0xD800 ||
                        
//  Codepoints outside the Unicode range are illegal
                        ( $mUcs4   >   0x10FFFF )) {

                        
trigger_error (
                                
' utf8_to_unicode: Illegal sequence or codepoint  ' .
                                    
' in UTF-8 at byte  ' . $i ,
                                
E_USER_WARNING
                            );

                        
return   FALSE ;

                    }

                    
if  ( 0xFEFF   !=   $mUcs4 ) {
                        
//  BOM is legal but we don't want to output it
                         $out []  =   $mUcs4 ;
                    }

                    
// initialize UTF8 cache
                     $mState   =   0 ;
                    
$mUcs4    =   0 ;
                    
$mBytes   =   1 ;
                }

            } 
else  {
                
/* *
                *((0xC0 & (*in) != 0x80) && (mState != 0))
                * Incomplete multi-octet sequence.
                
*/
                
trigger_error (
                        
' utf8_to_unicode: Incomplete multi-octet  ' .
                        
'    sequence in UTF-8 at byte  ' . $i ,
                        
E_USER_WARNING
                    );

                
return   FALSE ;
            }
        }
    }
    
return   $out ;
}


关于 utf8 和 Unicode 序列转换规则 你可以google。PHP里面不支持本地Unicode。也没有提供相关的函数。

 

 

posted @ 2010-12-17 19:15 暮夏 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值