zoukankan      html  css  js  c++  java
  • 在编码转错的情况下,如何恢复

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

    这里我只举一个我实际中遇到的一个例子。这个例子的情况是这样的,我在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过。

    代码
    <?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。也没有提供相关的函数。

  • 相关阅读:
    最近玩Bootstrap , 一些小工具 记录在案。
    测试word发表博客
    Linux at 定时任务
    Linux查看磁盘目录内存空间使用情况
    R生存分析AFT
    Accelerated Failure Time Models加速失效时间模型AFT
    Shell sleep指定延迟时间
    Shell脚本导入外部脚本内容
    Shell输入输出重定向
    Shell while
  • 原文地址:https://www.cnblogs.com/niniwzw/p/1909534.html
Copyright © 2011-2022 走看看