PHP:UTF-8 跟 Big5 的糾結

今天在公司處理到一個編碼的問題,由於有許多歷史包袱都是以 big5 下去開發,短時間內沒有辦法把所有系統轉換成 utf-8 的情況下,就經常要面對編碼轉換的問題,這邊記錄一下今天遇到問題的解法。

有個新的系統 B 必須要串接舊系統 A,B 是 utf-8 環境,A 是 big5 環境,目前設計是 B 透過 AJAX 來讀取 A 的資料,A 這邊吃的是經過 encodeURIComponent 處理過的資料,例如:「你好」 -> 「%A7%41%A6%6E」,前情提要到這邊。

小蛙的笨腦袋就開始想怎麼處理:

  • 該頁面拿到的資料都是 UTF-8,拿到之後用 iconv 轉 big5 先
  • 轉成 big5 之後,再做 encodeURIComponent 就好了
  • easy ~

整個串起來之後,正確的「你好」應該是「%A7%41%A6%6E」,小蛙卻得到「%EF%BF%BDA%EF%BF%BDn」,看起來好像差很大,於是想說可能是因為 php 要透過 javascript encodeURIComponent 來轉,雖然轉好 big5 但是又在 utf-8 的文本中印出導致的,於是開始往「一切都在 php 中轉好,javascript 直接拿到轉好值就丟出去」的方向進行,小蛙改變策略:

  • 在 php 中使用 iconv 把 UTF-8 轉成 big5
  • 在 php 中使用 rawurlencode 轉換
  • 把結果透過 javascript 去要資料

嗯,看起來應該是無懈可擊!哦哦哦哦哦哦 ~ 高興的尖叫起來,隔壁同事都看了小蛙一眼,但回傳的結果怪怪的啊,看一下後台 log,「你好」變成了「%A7A%A6n」,額外測試一個「外部」卻變成了「%A5~%B3%A1」,花惹發?嗯 ~ 港節好像比第一次得到的更接近正解一點(?),再來想想問題在哪,重新順一下流程好了,第一步跟第二步都做轉換的動作,其中一定有一個轉換失效了,要不第一個要不第二個(廢話)。

Google 了一下看到這篇「php 转码函数 你还在用iconv吗?」,既然懷疑轉換出問題,那就把他換掉看看,事情絕對沒有憨人所想的那麼簡單,結果沒變!沒變 … 記得以前有一次使用 base64 來做過這件事,正當打算要來試的時候,超猛同事走了過來問說發生什麼事,小蛙大概跟他講解了一下,他只回了一句,那你把 iconv 轉的東西印出來看就知道了。

看來出現新的曙光,Google 查到這篇「PHP convert string to hex and hex to string」,於是把 iconv 改成 mb_convert_encoding,然後再透過該文章中的 function 來做轉換,就可以成功轉換成系統 A 需要的格式了!

可喜可樂!留個備份,記錄超強同事的神蹟!

發現問題 (2018-10-16更新)

上面的作法用了一段時間後發現有些字串會解析成不一樣的東西,例如:帶卜辭實際上收到卻變成帶尸辭恆春變成恆柢小屯變成匕屯
以「帶卜辭」來當作轉換的例子
帶 => 「%B1a」
卜 => 「%A4R」
辭 => 「%C3%E3」

如果用上面的方法轉出來,轉出來會是全小寫,
帶 <= 「%b1a」
尸 <= 「%a4r」
辭 <= 「%c3%e3」

下方是把它改成全大寫
婢 <= 「%B1A」
卜 <= 「%A4R」
辭 <= 「%C3%E3」

但以上兩者都不對,因為原本的方法轉出來之後,就通通變成小寫,而大小寫卻對應到不一樣的字,造成這次的問題。

使用 rawurlencode (2018-10-16更新)

只要先把要轉換的文字,正確地轉到 big5 之後,透過 rawurlencode 來做編碼就可以編出正確的內容囉!

// 先把要的文字轉成 big5,再整個透過 rawurlencode 轉換
$txt = rawurlencode(mb_convert_encoding($txt, "big5", "utf-8"));

    發佈留言

    發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

    這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料