2010年11月10日 星期三

php上處理utf-16le檔案

最近要在php上讀取excel的檔案,因此有去找一些相關套件以及一些用法,因為要處理的檔案可能會有多國語言問題,所以一開始就規畫要用utf-8來編輯。

1. php excel reader
 這套件讀取excel很方便,但是不支援unicode,失敗。
2. php excel
這套件支援unicode,也不是很複雜,照理說應該用他,不過這東西可能裡面沒寫好,處理個2mb的excel檔,居然要吃掉256mb左右的記憶體,哪來這麼多記憶體給他吃,失敗。

後來就想說不然從CSV格式著手,結果excel輸出CSV不支援unicode,失敗
再來就試輸出成unicode的txt檔案,有成功,沒變亂碼,但是這是utf-16le的編碼,不是utf-8。

我很天真的想說,既然檔案是utf-16le,我以後要輸出的格式也是要utf-16le才能讓excel正確讀取,所以我乾脆整個網站架構改成utf-16le吧,反正瀏覽器支援讀取各式各樣編碼,但帶計膜架甘丹。

因為我採用lamp架構,但是php 5 &mysql 5.5都不支援utf-16,雖然有找出真的能在php上使用utf-16編碼的code,不過只有firefox上能正確顯示,IE上就掛了,所以別天真的想說弄個utf-16架構的網站,這大概只有純html才能辦的到。


確定網站端還是只能用utf-8之後,表示使用者丟utf-16le的檔案上來,我們要轉成utf-8在網站上處理,處理完後再轉成utf-16le回去給他,所以就是要處理utf-8 <=> utf-16le的問題了

上網google了很久,一堆說什麼使用iconv, mb_convert_encoding,utf8_encode,utf16_decode...之類的function,全部失敗,甚至還有人寫一大串在處理0xff, 0xfe之類的東西,但是一樣全部陣亡,根本沒一樣能跑出正常的東西,後來我乾脆把檔案直接用16進位方式打開,直接去看我的程式轉出來的東西跟轉檔程式跑的到底差在哪,看完之後發現檔頭都少人家2個byte,原來是該死的BOM,後來手動在要轉成utf-16的檔案上加上BOM就沒問題了。

只有utf-8允許可以沒有bom,utf-16, utf-32都是一定要有bom的,因為牽涉到little endian跟big endian的問題。
以下是我的code

function utf8_to_utf16($utf8_filename,$utf16_filename){
    $file = fopen($utf8_filename,"r");
    $write = fopen($utf16_filename,"w+");
    if($file){
        $buffer=chr(255).chr(254); //加入BOM
        fwrite($write,$buffer);
        while(($buffer = fgets($file)) != false){
            $buffer=iconv('UTF-8','UTF-16LE',$buffer);
            fwrite($write,$buffer);
        }
    }
}

function utf16_to_utf8($utf16_filename,$utf8_filename){
    $write = fopen($utf8_filename,"w+");
    $buffer=mb_convert_encoding(  file_get_contents( $utf16_filename ), 'UTF-8', 'UTF-16LE' );
    fwrite($write,$buffer);
}
為什麼2種轉法不一樣呢?
因為utf-8轉utf-16時會佔用比較多的記憶體,一次轉的話容易造成記憶體不足,所以分行轉,utf-16就沒這個問題,此外utf-8轉utf-16花的時間也比utf-16轉utf-8多,我測試轉一個12m的檔案,8轉16要花23秒,16轉8只要1秒...

另外,用這個方式轉出來的utf-8是有bom的,要去除的話請自己來 XD

3 則留言:

匿名 提到...

太感謝你了
我弄了兩天也都在找這個東西
用過C++會失敗
也用過線上轉換的都無效

最後總算找到你的文章
真的很謝謝你 ^^

jikker 提到...

很高興能幫到你 ^^

RonaldLin 提到...

我也用到了,是php,就缺了 chr(255).chr(254),搞了一個多小時。感謝