zoukankan      html  css  js  c++  java
  • PHP源文件编码与变量编码的判断

    PHP中文乱码的原因通常在于header()设置的编码与PHP文件的编码不一致。

    PHP文件的编码影响了其变量的编码,在默认的情况下,PHP文件是什么编码,其变量也就是什么编码。

    而当将变量我们输出到网页时,如果与header()设置的编码不一致,也就产生乱码。

    虽然变量最开始时与PHP源文件一致,但是有些时候,一些方法(函数)的操作,会改变变量的编码方式。

    另外,有些接收性的变量,不一定与PHP源文件的编码或header()的编码一样。

    这样,当变量编码与PHP源文件不同时,我们就需要进行一步类似于以下的操作: 

    $myVar=   iconv("$myVar当前编码",“PHP源文件的编码”,$myVar); 

    即将变量转化为与PHP源文件一致的编码。

    很多时候,这句代码是出现在方法(函数)中的,如果需要适应PHP源文件是UTF-8与GBK。即无论源文件是UTF-8或GBK,该方法(函数)代码都不需要进行修改,并且如果需要,可以在调用端加入一个输入性编码来表示当前变量编码。

    这时候,我们不是在方法(函数)中直接写死成:

    $myVar = iconv("$myVar当前编码","UTF-8",$myVar)  或  $myVar = iconv("$myVar当前编码","GBK",$myVar),而是一般作以下类似的处理(只考虑GB2312及UTF-8编码):

    function doSth($myVar,$input_charset){
          $is_utf8 = isutf8();//判断PHP源文件是否UTF-8编码
          
           if($is_utf8){
                if(strtoupper($input_charset) != 'UTF-8'){
                    $myVar = iconv($input_charset,'UTF-8',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($input_charset) != 'GB2312') {
                     $myVar = iconv($input_charset, 'GB2312',$myVar);
                }
           }
    
            //do sth with $myVar ...
    
    }

    在没有自动判断变量(一般指字符串)是哪种编码的情况下,客户端调用就只能用:doSth($myVar,'UTF-8')或doSth($myVar,‘GB2312’)显然,每次调用都需要附带编码。

    为了节省麻烦,通常有两种手段,一是使用判断变量(字符串)编码的方法自动判断变量(字符串)编码(需要自己实现),二是给予默认值。

    这里先说二,对$input_charset给个默认值,由于需要兼容UTF-8或GBK的源文件,所以默认值给‘UTF-8’或'GB2312'都是不太合适的。

    最好的办法是给空字符串作为默认值,在方法内部判断,当为空字符串时,让它的默认值与PHP源文件的编码一致(只考虑GB2312及UTF-8编码)。

    function doSth($myVar,$input_charset=''){
          $is_utf8 = isutf8();
          if(empty($input_charset)){
              $input_charset = $is_utf8 ? 'UTF-8' : 'GB2312';
          }
          
           if($is_utf8){
                if(strtoupper($input_charset) != 'UTF-8'){
                    $myVar = iconv($input_charset,'UTF-8',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($input_charset) != 'GB2312') {
                     $myVar = iconv($input_charset, 'GB2312',$myVar);
                }
           }
    
            //do sth with $myVar ...
    
    }

    这时,客户端调用就用:doSth($myVar);   如果遇到乱码,就证明变量实际编码与默认编码(PHP文件编码)不一致。这时不能使用默认值了,只能指定实际编码,如:doSth($myVar,'UTF-8');  或 doSth($myVar,'GB2312');

    PHP源文件为UTF-8编码,$myVar实际编码也为UTF-8编码,header()设置字符编码也为UTF-8编码,doSth($myVar);执行过程不会进行编码转换,运行结果不产生乱码。

    PHP源文件为UTF-8编码,$myVar实际编码也为UTF-8编码,header()设置字符编码也为GBK编码,doSth($myVar);执行过程不会进行编码转换,运行结果产生乱码

    因为在没有进行输出的编码转换的时候,默认的输出结果是与PHP源文件保持一致的,所以,也可以添加另外一个参数,即输出编码$output_charset(让其默认为空,也处理为默认为PHP文件编码),

    此时方法改写为(只考虑GB2312及UTF-8编码):

    function doSth($myVar,$input_charset='',$output_charset=''){
          $is_utf8 = isutf8();
          if(empty($input_charset)){
              $input_charset = $is_utf8 ? 'UTF-8' : 'GB2312';
          }
          
           if($is_utf8){
                if(strtoupper($input_charset) != 'UTF-8'){
                    $myVar = iconv($input_charset,'UTF-8//IGNORE',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($input_charset) != 'GB2312') {
                     $myVar = iconv($input_charset, 'GB2312//IGNORE',$myVar);
                }
           }
    
          if(empty($output_charset)){
              $output_charset = $is_utf8 ? 'UTF-8' : 'GB2312';
          }
    
           if($is_utf8){
                if(strtoupper($output_charset) != 'UTF-8'){
                    $myVar = iconv('UTF-8',$output_charset.'//IGNORE',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($output_charset) != 'GB2312') {
                     $myVar = iconv('GB2312',$output_charset.'//IGNORE',$myVar);
                }
           }
    
            //do sth with $myVar ...
    
    }

    要使上边结果不乱码,可以调用:doSth($myVar,'','GB2312');  

    PHP源文件为UTF-8编码,$myVar实际编码为GB2312编码,doSth($myVar, 'GB2312'); 可以产生UTF-8编码的l输出结果,然后再视header()设置的字符编码,决定输出的编码参数。

    如果header()设置编码为UTF-8,那么:doSth($myVar,'GB2312');执行结果不会产生乱码。

    如果header()设置编码为GB2312,那么:doSth($myVar,'GB2312','GB2312') 或 doSth($myVar) 也不会产生乱码。

    至此,先总结一下:

    doSth($myVar,$input_charset='',$output_charset='')  对于UTF-8编码的源文件,其调用方式:
    doSth($myVar);//当$myVar为UTF-8,header设置的也为UTF-8时
    doSth($myVar,'','GB2312');//当$myVar为UTF-8,header设置的为GB2312时
    doSth($myVar,'GB2312');//当$myVar为GB2312,header设置为UTF-8时
    doSth($myVar,'GB2312','GB2312');//当$myVar为GB2312,header设置为GB2312时。不过,由于两次转换的原因,也可以直接用:doSth($myVar);

    doSth($myVar,$input_charset='',$output_charset='') 对于GB2312编码的源文件,其调用方式:
    doSth($myVar);//当$myVar为GB2312,header()设置也为GB2312时
    doSth($myVar,'','UTF-8');//当$myVar为GB2312, header()设置为UTF-8时
    doSth($myVar,'UTF-8');//当$myVar为UTF-8,header设置为GB2312时
    doSth($myVar,'UTF-8','UTF-8');//当$myVar为UTF-8,header设置也为UTF-8时。不过由于两次转换的原因,也可以直接用:doSth($myVar);

    至此,可以用5种方式来进行调用,并且可以发现:
    如果header()与变量实际编码一致或者header()与PHP源文件编码一致,则无需指定输出编码。
    如果源文件编码与变量实际编码一致的话,则无需指定输入编码。
    UTF-8文件编码时,输入编码或输出编码可能需要指定为'GB2312'
    GB2312文件编码时,输入编码或输出编码可能需要指定为'UTF-8'

    由于PHP虽然可以判断源文件编码,但对于header()设置了什么编码是无法通过代码判断的,所以$output_charset目前而言,是无法省略的,但$input_charset(即变量的编码),是可以通过代码判断的(虽然目前很多代码无法做到完美)

    function doSth($myVar,$input_charset='',$output_charset='')  
    在有办法获取变量(字符串)的编码时,可以改写成:
    fu
    nction doSth($myVar, $output_charset='')

    function doSth($myVar,$output_charset=''){
          $is_utf8 = isutf8(); //判断PHP文件编码
          $input_charset = var_charset($myVar);//判断变量编码
          
           if($is_utf8){
                if(strtoupper($input_charset) != 'UTF-8'){
                    $myVar = iconv($input_charset,'UTF-8//IGNORE',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($input_charset) != 'GB2312') {
                     $myVar = iconv($input_charset, 'GB2312//IGNORE',$myVar);
                }
           }
    
          if(empty($output_charset)){
              $output_charset = $is_utf8 ? 'UTF-8' : 'GB2312';
          }
    
           if($is_utf8){
                if(strtoupper($output_charset) != 'UTF-8'){
                    $myVar = iconv('UTF-8',$output_charset.'//IGNORE',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($output_charset) != 'GB2312') {
                     $myVar = iconv('GB2312',$output_charset.'//IGNORE',$myVar);
                }
           }
    
            //do sth with $myVar ...
    
    }

    下边将对isutf8()方法及var_charset($str)方法进行实现:

    isutf8()方法还是比较容易的,下边进行探测实现:

    function isutf8(){
         //返回true或false
    }

    汉字“一” 在PHP源文件为UTF-8时,调用:json_encode("一“) 会正确的返回含UNICODE编码的字符串     "u4e00"   

    而在PHP源文件为GBK时,调用:json_encode("一“) 会不正确的返回     "u04bb"  

    所以,我们直接可以用此方法来判断源文件编码是否UTF-8:

    function isutf8(){
         return json_encode('一')=='"u4e00"';
    }

    当然,还有其它各种手段来判断,原理类似:

    function isutf8(){
        return json_decode('"u4e00"') == "一";
    }
    
    function isutf8(){
        return bin2hex('一')=='e4b880';
    }
    
    function isutf8(){
        return strlen('一')==3;
    }
    
    function isutf8(){
        return preg_match('/xe4xb8x80/','一');
    }
    
    function isutf8(){
        return pack('a','一')==chr(0xe4);
    }
    
    function isutf8(){
        return ord('一')==0xe4;
    }
    
    function isutf8(){
        return chr(0xe4).chr(0xb8).chr(0x80)=='一';
    }
    
    function isutf8(){
        return chr(0xe4) =='一'[0];
    }
    
    function isutf8(){
        return "xe4" ==substr('一',0,1);
    }

     类似地,还可以参照地实现isgbk()等。

    var_charset($str)方法目前比较流行于网络的实现为:

    function var_charset($string) {
        $charset = ['ASCII','GB2312','GBK','UTF-8'];
        foreach($charset as $c) {
            if ($string === @iconv($c, $c . '//IGNORE', $string)) {
               return $c;
            }
        }
        return null;
    }

    或者:

    function var_charset($string){
        return [
            '0'=>NULL,
            'ASCII'=>'ASCII',
            'EUC-CN'=>'GB2312',
            'CP936'=>'GBK',
            'UTF-8'=>'UTF-8'
        ][mb_detect_encoding($string,array('ASCII','GB2312','GBK','UTF-8'))];
    }

    iconv()以及mb_detect_encoding()来判断的方式其实不是特别完美,有时不准确。

    另外还有这种判断UTF-8的对于非中文环境来说,确实更准确的,但对于中文环境来说,也是不准确的:

    function is_utf8str($string) {
        // From http://w3.org/International/questions/qa-forms-utf-8.html
        return preg_match('%^(?:
            [x09x0Ax0Dx20-x7E] # ASCII
            | [xC2-xDF][x80-xBF] # non-overlong 2-byte
            | xE0[xA0-xBF][x80-xBF] # excluding overlongs
            | [xE1-xECxEExEF][x80-xBF]{2} # straight 3-byte
            | xED[x80-x9F][x80-xBF] # excluding surrogates
            | xF0[x90-xBF][x80-xBF]{2} # planes 1-3
            | [xF1-xF3][x80-xBF]{3} # planes 4-15
            | xF4[x80-x8F][x80-xBF]{2} # plane 16
            )*$%xs', $string);
    }
    
    function var_charset($string){
        return is_utf8str($string) ? 'UTF-8' : 'GB2312';
    }

    另外,自己实现了这种,针对中文环境来实现的判断UTF-8,同时也可以适应英文环境(中文环境中不使用第二个参数):

    function is_utf8str($str,$utf8all2bits=false){
        $bit2 = true;
        $bit3 = false;
        $bit4 = false;
        $allCN = array();
        $len = strlen($str);
        for($i=0;$i<$len;$i++){
            $c1 = substr($str,$i,1);
            if(ord($c1)<=0x7F) continue;
            if(ord($c1)>=0xFF) return false;
            $flag1_2 = ord($c1)>=0xc0 && ord($c1)<=0xdf;
            $flag1_3 = ord($c1)>=0xe0 && ord($c1)<=0xef;
            $flag1_4 = ord($c1)>=0xf0 && ord($c1)<=0xf7; 
            if(!($flag1_2 || $flag1_3 || $flag1_4) || $i==$len-1) return false;
            $c2 = substr($str,$i+1,1);
            
            $flag2 = ord($c2)>=0x80 && ord($c2)<=0xbf;
            if(!$flag2) return false;
            if($flag1_2){
                $bit2 = true;
                $allCN[] = (ord($c1)>=0xB0 && ord($c1)<=0xF7 && ord($c2)>=0xA1 && ord($c2)<=0xFE) ? 1 : 0;
                
                if($i==$len-2) {if($bit3 || $bit4) return true;
                    $N = count($allCN);
                    if($N>0){
                        for($n=0;$n<$N;$n++){
                            if($allCN[$n]!=1){
                                return true;
                            }
                        }
                    }
                    return $utf8all2bits;
                }
                $i=$i+1;
            }else{
                if($i==$len-2) return false;
                $c3 = substr($str,$i+2,1);
                $flag3 = ord($c3)>=0x80 && ord($c3)<=0xbf;
                if(!$flag3) return false;
                if($flag1_3){
                    $bit3 = true;
                    if($bit2 || $bit4 || $i==$len-3) return true;
                    $i=$i+2;
                }else{
                    $bit4 = true;
                    if($i==$len-3) return false;
                    $c4 = substr($str,$i+3,1);
                    $flag4 = ord($c4)>=0x80 && ord($c4)<=0xbf;
                    if(!$flag4) return false;
                    if($bit2 || $bit3 || $i==$len-4) return true;
                    $i=$i+3;
                }
            }
        }
        
        return true;
    }
    
    function var_charset($string){
        return is_utf8str($string) ? 'UTF-8' : 'GB2312';
    }

    解决守isutf8()和var_charset()后,doSth()的调用如下:

    doSth($myVar,$output_charset='')  对于UTF-8编码的源文件,其调用方式:
    doSth($myVar);//当header设置为UTF-8时
    doSth($myVar,'GB2312');//当header设置的为GB2312时

    doSth($myVar,$output_charset='') 对于GB2312编码的源文件,其调用方式:
    doSth($myVar);//当header设置的也为GB2312时
    doSth($myVar,'UTF-8');//当header设置的为UTF-8时


    至此,可以用3种方式来进行调用,并且可以发现:
    如果header()与变量实际编码一致或者header()与PHP源文件编码一致,则无需指定输出编码。
    UTF-8文件编码时,输出编码可能需要指定为'GB2312'
    GB2312文件编码时,输出编码可能需要指定为'UTF-8'



    所以,乱码的时候,直接更换一下输出编码就可以了。三种调用方式总结:

    doSth($myVar); //PHP源文件与header()设置一致时

    doSth($myVar,'GB2312');//PHP源文件为UTF-8,header()设置为GB2312时

    doSth($myVar,'UTF-8'); //PHP源文件为GB2312,header()设置为UTF-8时

    这样的话,当header()设置的该编码与PHP源文件不一致的时候,header()设置了什么编码,$output_charset就指定为什么编码,这样就不会有乱码输出。


    上边的doSth($myVar,$output_charset)假定了对$myVar需要进行页面输出的情况,如果只对$myVar进行操作而不进行页面输出的话,可以简化(只考虑GB2312及UTF-8编码):
    function doSth($myVar,$output_charset=''){
          $is_utf8 = isutf8(); //判断PHP文件编码
          $input_charset = var_charset($myVar);//判断变量编码
          
           if($is_utf8){
                if(strtoupper($input_charset) != 'UTF-8'){
                    $myVar = iconv($input_charset,'UTF-8//IGNORE',$myVar);
                }
           } else {//php-GBK
                if(strtoupper($input_charset) != 'GB2312') {
                     $myVar = iconv($input_charset, 'GB2312//IGNORE',$myVar);
                }
           }
    }

    自动判断变量编码,不考虑输出的调用,最为简单:     doSth($myVar);



  • 相关阅读:
    事物
    性能优化
    eclipse中如何查看一个android模拟器的内部文件
    Android无线测试之—UiAutomator UiDevice API介绍二
    Android无线测试之—UiAutomator UiDevice API介绍一
    Linux最大打开文件描述符数
    Android无线测试之—UiAutmator运行命令介绍与快速调试
    Android无线测试之—UiAutomator编译与运行测试代码
    Android无线测试之—Genymotion配置过程中常见问题
    Android无线测试之—Genymotion模拟器环境搭建
  • 原文地址:https://www.cnblogs.com/dreamyoung/p/php-var_charset.html
Copyright © 2011-2022 走看看