顯示具有 Component 標籤的文章。 顯示所有文章
顯示具有 Component 標籤的文章。 顯示所有文章

2009年1月15日 星期四

去除UTF-8 BOM表頭的InputStreamReader

如果你曾經使用Java遇過讀一些文字檔前面出現??等亂碼的話, 表示該份文件有加入bom(byte order mark), 基本上bom的用途原本是用來讓程式辨別該份文檔的編碼格式, Microsoft在Windows 2000以後的Notepad存UTF-8的檔案會加上 BOM(Byte Order Mark, U+FEFF), 主要是因為UTF-8和ASCII是相容的, 為了避免使用者自己忘記用甚麼存, 造成UTF-8檔案用 ASCII 模式開看到是亂碼, 所以在檔案最前面加上BOM.

看到這裡可能有很多人會開始%#~!微軟...

但是碰到問題總是得解決的, 下面這個改寫InputStreamReader的UnicodeInputStreamReader就能夠讓你免去上述煩惱, 所有功能仍然和InputStreamReader相同, 只是在constructor中加入判斷bom表頭的機制, 使用PushbackInputStream當讀取到相關格式時能夠移動檔案開始位置.

使用方式: BufferedReader in = new BufferedReader(new UnicodeInputStreamReader(new FileInputStream(file), encode));

UnicodeInputStreamReader:

package com.progking.io;

import java.io.IOException;
import java.io.PushbackInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import sun.nio.cs.StreamDecoder;

public class UnicodeInputStreamReader extends InputStreamReader
{
   private static final int BOM_SIZE = 4;
   private final StreamDecoder decoder;
   private PushbackInputStream pushBack;
   private String encode;
   private String defaultEnc;

   public UnicodeInputStreamReader(InputStream input, String defaultEnc) throws UnsupportedEncodingException
   {
       super(input);
       try {
           this.defaultEnc = defaultEnc;
           pushBack = new PushbackInputStream(input, BOM_SIZE);
           init();
       } catch (Exception e) {
           e.printStackTrace();
       }
       decoder = StreamDecoder.forInputStreamReader(pushBack, this, encode);
   }

   private void init() throws IOException
   {
       byte[] bom = new byte[BOM_SIZE];
       int n, unread;

       // 初始讀取一次
       n = pushBack.read(bom, 0, bom.length);

       // 比對表頭
       if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
           encode = "UTF-32BE";
           unread = n - 4;
       } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
           encode = "UTF-32LE";
           unread = n - 4;
       } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
           encode = "UTF-8";
           unread = n - 3;
       } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
           encode = "UTF-16BE";
           unread = n - 2;
       } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
           encode = "UTF-16LE";
           unread = n - 2;
       } else {
           // 如果沒有找到任何表頭, 則退回長度等於原先總長
           encode = defaultEnc;
           unread = n;
       }
       System.out.println("has BOM=" + ((unread == n) ? false : true) + ", encode=" + encode + ", read=" + n + ", unread=" + unread);
       // 計算應該退回多少byte
       if (unread > 0)
           pushBack.unread(bom, (n - unread), unread);
   }

   public String getEncoding()
   {
       return encode;
   }

   public int read() throws IOException
   {
       return decoder.read();
   }

   public int read(char cbuf[], int offset, int length) throws IOException
   {
       return decoder.read(cbuf, offset, length);
   }

   public boolean ready() throws IOException
   {
       return decoder.ready();
   }

   public void close() throws IOException
   {
       decoder.close();
   }
}

2008年12月24日 星期三

用php做出類似Google的字詞驗證圖片

雖然日前Google字詞驗證與CAPTCHA都已經宣告被破解, 但還是用php來寫出一個類似的介面, php圖片要扭曲必須要用像素位移的方式, 所以出來的圖片會有一點沙沙的感覺。 其實也可以使用其他附加軟體進行扭曲的動作, 但那樣可攜性就相對降低了。
使用時請在這支php同資料夾加上font資料夾, 裡面放置要產生驗證碼的字型, 幾個都可以, 會隨機挑選, 驗證碼存在$_SESSION['vCode']中, 格式為 驗證碼|時間, 比對時要explode("|", $_SESSION['vCode'])。

也可以加上干擾線條與點數, 變的更難辨識。

測試檔案打包下載

<?php

session_start();   // if header already send, change output_buffering = On at php.ini. otherwise save as UTF-8 without BOM.

$vi = new vCodeImage();
$vi -> SetImage(2,7,130,60,120,1);

class vCodeImage
{
    var $mode;            // 1.文字模式, 2.字母模式, 3.文字字母混合模式, 4.其他文字字母優化模式
    var $v_num;            // 驗證碼個數
    var $img_w;            // 圖像寬度
    var $img_h;            // 圖像�度
    var $int_pixel_num; // 干擾像數個數
    var $int_line_num;    // 干擾線條數量
    var $font_dir;        // 字型文件路徑
    var $border;        // 圖像邊框
    var $borderColor;    // 圖像邊框顏色

    function SetImage($mode, $v_num, $img_w, $img_h, $int_pixel_num, $int_line_num, $font_dir='font', $border=false, $borderColor='0,0,0')
    {
        if(!isset($_SESSION['vCode'])){
            session_register('vCode');
        }
        $_SESSION['vCode'] = "";

        $this -> mode = $mode;
        $this -> v_num = $v_num;
        $this -> img_w = $img_w;
        $this -> img_h = $img_h;
        $this -> int_pixel_num = $int_pixel_num;
        $this -> int_line_num = $int_line_num;
        $this -> font_dir = $font_dir;
        $this -> border = $border;
        $this -> borderColor = $borderColor;
        $this -> GenerateImage();
    }

    function GetChar($mode)
    {
        if($mode == "1"){
            $ychar = "0,1,2,3,4,5,6,7,8,9";
        }else if($mode == "2"){
            $ychar = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z";
        }else if($mode == "3"){
            $ychar = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z";
        }else{
            $ychar = "3,4,5,6,7,8,9,a,b,c,d,h,k,p,r,s,t,w,x,y";
        }
        return $ychar;
    }
 
    function RandColor($rs, $re, $gs, $ge, $bs, $be)
    {
        $r = mt_rand($rs, $re);
        $g = mt_rand($gs, $ge);
        $b = mt_rand($bs, $be);
        return array($r, $g, $b);
    }
 
    function GenerateImage()
    {
        $fonts = scandir($this -> font_dir);
        $ychar = $this -> GetChar($this -> mode);
        $list = explode(",", $ychar);
        $cmax = count($list) - 1;
        $fmax = count($fonts) - 2;
        $fontrand = mt_rand(2, $fmax);
        $font = $this -> font_dir."/".$fonts[$fontrand];

        // 驗證碼
        $v_code = "";
        for($i = 0; $i < $this-> v_num; $i++){    
            $randnum = mt_rand(0, $cmax);
            $this_char = $list[$randnum];
            $v_code .= $this_char;
        }

        // 扭曲圖形
        $im = imagecreatetruecolor ($this -> img_w + 50, $this -> img_h);
        $color = imagecolorallocate($im, 32, 81, 183);
        $ranum = mt_rand(0, 2);
        if($ranum == 0){
            $color = imagecolorallocate($im, 32, 81, 183);
        }else if($ranum == 1){
            $color = imagecolorallocate($im, 17, 158, 20);
        }else{
            $color = imagecolorallocate($im, 196, 31, 11);
        }
        imagefill($im, 0, 0, imagecolorallocate($im, 255, 255, 255) );
        imagettftext ($im, 24, mt_rand(-6, 6), 10, $this -> img_h * 0.6, $color, $font, $v_code);

        // 干擾線條
        for($i = 0; $i < $this -> int_line_num; $i++){
            $rand_color_line = $color;
            imageline($im, mt_rand(2,intval($this -> img_w/3)), mt_rand(10,$this -> img_h - 10), mt_rand(intval($this -> img_w - ($this -> img_w/3) + 50),$this -> img_w), mt_rand(0,$this -> img_h), $rand_color_line);
        }

        $ranum = mt_rand(0, 1);
        $dis_range = mt_rand(8, 12);
        $distortion_im = imagecreatetruecolor ($this -> img_w * 1.5 ,$this -> img_h);        
        imagefill($distortion_im, 0, 0, imagecolorallocate($distortion_im, 255, 255, 255));
        for ($i = 0; $i < $this -> img_w + 50; $i++) {
            for ($j = 0; $j < $this -> img_h; $j++) {
                $rgb = imagecolorat($im, $i, $j);
                if($ranum == 0){
                    if( (int)($i+40+cos($j/$this -> img_h * 2 * M_PI) * 10) <= imagesx($distortion_im) && (int)($i+20+cos($j/$this -> img_h * 2 * M_PI) * 10) >=0 ) {
                        imagesetpixel ($distortion_im, (int)($i+10+cos($j/$this -> img_h * 2 * M_PI - M_PI * 0.4) * $dis_range), $j, $rgb);
                    }
                }else{
                    if( (int)($i+40+sin($j/$this -> img_h * 2 * M_PI) * 10) <= imagesx($distortion_im) && (int)($i+20+sin($j/$this -> img_h * 2 * M_PI) * 10) >=0 ) {
                        imagesetpixel ($distortion_im, (int)($i+10+sin($j/$this -> img_h * 2 * M_PI - M_PI * 0.4) * $dis_range), $j, $rgb);
                    }
                }
            }
        }

        // 干擾像素
        for($i = 0; $i < $this -> int_pixel_num; $i++){
            $rand_color_pixel = $color;
            imagesetpixel($distortion_im, mt_rand() % $this -> img_w + 20, mt_rand() % $this -> img_h, $rand_color_pixel);
        }

        // 繪製邊框
        if($this -> border){
            $border_color_line = $color;
            imageline($distortion_im, 0, 0, $this -> img_w, 0, $border_color_line); // 上橫
            imageline($distortion_im, 0, 0, 0, $this -> img_h, $border_color_line); // 左豎
            imageline($distortion_im, 0, $this -> img_h-1, $this -> img_w, $this -> img_h-1, $border_color_line); // 下橫
            imageline($distortion_im, $this -> img_w-1, 0, $this -> img_w-1, $this -> img_h, $border_color_line); // 右豎
        }

        imageantialias($distortion_im, true); // 消除鋸齒

        $time = time();
        $_SESSION['vCode'] = $v_code."|".$time; // 把驗證碼與時間賦與給 $_SESSION[vCode], 時間欄位可以驗證��否超時

        // 生成圖像給瀏覽器
        if (function_exists("imagegif")) {
            header ("Content-type: image/gif");
            imagegif($distortion_im);
        }else if (function_exists("imagepng")) {
            header ("Content-type: image/png");
            imagepng($distortion_im);
        }else if (function_exists("imagejpeg")) {
            header ("Content-type: image/jpeg");
            imagejpeg($distortion_im, "", 80);
        }else if (function_exists("imagewbmp")) {
            header ("Content-type: image/vnd.wap.wbmp");
            imagewbmp($distortion_im);
        }else{
          die("No Image Support On This Server !");
        }

        imagedestroy($im);
        imagedestroy($distortion_im);
    }
}
?>