2013年12月28日 星期六

PHP讀取BIG5編碼的CSV檔

之前寫過 PHP 使用 fgetcsv() 讀取中文 CSV 檔,須設定 setlocale 避免亂碼
http://xyz.cinc.biz/2012/11/phpfgetcsv.html

但如果 CSV 檔是 BIG5 編碼,當中文字中有包含反斜線(\)字元的衝碼字,
可能會使 fgetcsv() 解析出來的資料欄位數量不正確。
查看 fgetcsv() 預設的參數 http://www.php.net/fgetcsv
array fgetcsv ( resource $handle [, int $length = 0 [, string $delimiter = ',' [, string $enclosure = '"' [, string $escape = '\\' ]]]] )

可發現第 5 個脫逸參數預設是反斜線(\),所以當解析到包含反斜線(\)的 BIG5 中文字時,
雖然可以解析,但解析出來的資料不是預期的,該行解析出的欄位數也會是錯的。

最快的處理方式,可以直接修改第五個參數,改成其他CSV檔中不會出現的字元。
但是,第 5 個脫逸參數在 PHP5.3.0 後才可以修改 (5.3.0 The escape parameter was added )。

如果是用比較早的版本,沒有第 5 個脫逸參數可以設定。
不過,網路上有人寫了一個 function,可以用來處理 BIG5 中文的 CSV 檔。
使用上就跟原本的 fgetcsv() 一樣,只是改成這個另外寫的 __fgetcsv()

/**
 * 對CSV進行處理
 * @param resource handle
 * @param int length
 * @param string delimiter
 * @param string enclosure
 * @return 文件內容或FALSE。
 */
function __fgetcsv(&$handle, $length = null, $d = ",", $e = '"') {
    $d = preg_quote($d);
    $e = preg_quote($e);
    $_line = "";
    $eof = false;
    while ($eof != true) {
        $_line .= (empty($length) ? fgets($handle) : fgets($handle, $length));
        $itemcnt = preg_match_all('/' . $e . '/', $_line, $dummy);
        if ($itemcnt % 2 == 0) {
            $eof = true;
        }
    }
    $_csv_line = preg_replace('/(?: |[ ])?$/', $d, trim($_line));

    $_csv_pattern = '/(' . $e . '[^' . $e . ']*(?:' . $e . $e . '[^' . $e . ']*)*' . $e . '|[^' . $d . ']*)' . $d . '/';
    preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
    $_csv_data = $_csv_matches[1];

    for ($_csv_i = 0; $_csv_i < count($_csv_data); $_csv_i++) {
        $_csv_data[$_csv_i] = preg_replace("/^" . $e . "(.*)" . $e . "$/s", "$1", $_csv_data[$_csv_i]);
        $_csv_data[$_csv_i] = str_replace($e . $e, $e, $_csv_data[$_csv_i]);
    }
    return empty($_line) ? false : $_csv_data;
}


其他:
BIG5衝碼字 ASCII(5C) == “\”

A45C么 AE5C娉 B85C稞 C25C擺 A55C功
AF5C珮 B95C鈾 C35C黠 A65C吒 B05C豹
BA5C暝 C45C孀 A75C吭 B15C崤 BB5C蓋
C55C髏 A85C沔 B25C淚 BC5C墦 C65C躡
A95C坼 B35C許 BD5C穀 AA5C歿 B45C廄
BE5C閱 AB5C俞 B55C琵 BF5C璞 AC5C枯
B65C跚 C05C餐 AD5C苒 B75C愧 C15C縷


BIG5衝碼字 ASCII(7C) == “|”

AA7C泜 B47C揉 A87C育 BE7C魯 B27C琍
BC7C慝 C67C鸛 A97C尚 B37C逖 BD7C罵
A77C坑 B17C悴 BB7C誡 C57C疊 A67C帆
B07C院 BA7C漏 C47C辮 AB7C咽 B57C稅
BF7C糕 AC7C洱 B67C閏 C07C嚐 AD7C迢
B77C會 C17C舉 A47C弋 AE7C徑 B87C腮
C27C甕 A57C四 AF7C砝 B97C頌 C37C牘


參考:
http://blog.csdn.net/shilian_h/article/details/4371051
http://www.ptt.cc/man/PHP/D979/DC1A/D6E0/M.1164457747.A.87E.html

沒有留言:

張貼留言