最近 gslin 的 blog 因為 MySQL 資料庫的 encoding 問題,發生異常現象,只好從零開始。無獨有偶地,Pesty 最近也處理了 MySQL 使用 UTF-8 的相關問題。
這些問題之所以會發生,是因為越來越多的 web application 套件使用 UTF-8 儲存資料,但大家在設定 MySQL 時卻都沒有注意到這點,仍然使用預設的 latin1 作為資料庫的 encoding。Web application 餵入 UTF-8 資料,MySQL 卻當作 latin1 在讀,雖然 web application 讀回來的時候,因為剛好是反向處理,資料看似沒有任何異常,但實際上 collation 是錯的,當進行移機、備份甚至轉換 web application 時,也都會發生問題。
我一年多前也有遇到類似的問題,原本在公司有個用 phpBB2 架的 forum,採用 big5 編碼,但實際上 mysql-3.23 裡是用 latin1 存。想要轉換成 UTF-8,移機到 FreeBSD 5 上改用 mysql-4.1,就發生問題了。我那時沒有解決,所以 [...]
PalmIsLife 的阿輝在這篇《[交流] RSS Web Host 很適合在 PDA 上瀏覽》介紹了如何使用他的 Treo 650 看 Bloglines,我才知道,原來 Bloglines 有提供 mobile 版本,如果搭配 data stream 吃到飽的 3G 門號的話,非常地吸引人。 所以我就提了兩個問題如下:
我直接用 TH-55 連上 Bloglines,畫面仍然是分 <frame> 的,並沒有出現適合 PDA 閱讀的畫面版本。請問阿輝是直接連就可以了嗎?還是用 PDA 專用的 Bloglines 網址?我的 TH-55 的瀏覽器是 NetFront v3.1 r2.0.26 (en)。
大多數的 Blog 以及 Bloglines 的網頁皆是採用 UTF-8 編碼。請問阿輝的 PDA 畫面能夠正常顯示中文,是因為有裝「Unicode 補完計畫」的關係嗎?還是用了其他特殊的技巧?
阿輝的解答是,對於第一個問題,他直接上就可以了,所以 Bloglines 沒辦法偵測我用的 NetFront v3.1,自動切換成 mobile [...]
在工作上,經常會碰到萬碼奔騰的狀況,常搞的我一個頭兩個大,還好經過一年多的努力,處理 i18n 的程式庫大致有了個基礎,雖不完整,但也堪用。我有試著讓這堆 i18n 的程式儘可能地一致有條理,但邊造槍邊上戰場,還是有很多地方雜草叢生,旁枝錯節,無法達到完美和諧。所以我也只能盡量,將「萬碼奔騰」化約為「五碼諧和」,便是其中的一個嘗試。
世界上的語言千萬種,應用在電腦上,各家都要有自己的一套,因而造成了「萬碼奔騰」的現況。雖然 Unicode 包容萬碼的理想極其崇高偉大,但是革命尚未成功,同志仍須努力。一天這個世界還不能大一統,身為師程工的我們,就必須要身先士卒,代替普羅大眾,面對萬碼侵襲。
還好,我們有「化約」這個武器,可以化繁為簡,萬宗歸五,將「萬碼」化約成「五碼」,挑出重點,各個擊破。哪五碼?RAW、MBCS、WCCS、U8CS 以及 CLCS 這五碼,各有各的花名,招式相同的,大家一條褲子一起穿,招式不同的,那就各顯本事,各展奇才。口說無憑,有表有真相。
編碼
字元
字串
說明
花名
型別
花名
型別
RAW
-
char
str
char*
無招勝有招,赤裸裸的 bytes 專門演出無碼劇。本應使用 byte_t 為其型別,但為與大多數的舊有 C 程式介面相容,故仍沿用 char。
MBCS
mbc
char*
mbs
char*
用多個 bytes 表達一個 character 的編碼形式。某些 MBCS 需要 shift-state 的支援。對 C/C++ 來說,MBCS 對應到目前唯一的 global locale。
WCCS
wcc
wchar_t
wcs
wchar_t*
由 OS/Compiler 廠商決定的固定長度 Unicode 編碼,無法跨平台,兼有包容與速度的優勢。
U8CS
u8c
char*
u8s
char*
即 UTF-8,為近年流行的跨平台之不固定長度 Unicode 編碼,未來的烏托邦,便是只剩下 WCCS 與 U8CS 這兩種碼的大一統世界。雖然現在還不是,但我們必須向著這個方向走。
CLCS
lcc
byte_t*
lcs
byte_t*
CLCS 為 Custom Locale Character Set 的縮寫,即指定 locale 的編碼方式。當其他四碼都不行時,就只有靠他了。
為了分辨彼此,各招的名稱,必須內含各碼的花名。舉例來說,光就「長度」這一項,就可以分化成三個變化:有多少個 bytes、有多少個 characters 以及畫在終端機螢幕上時,佔用多少個基本寬度。將上表套用這三個變化,就成了以下各招的招型:
有多少個 bytes:除了 RAW [...]
今天搞了一個下午,結論就是這個教訓:小心繼承 iostream 的邊際效應 (side effect)。白話一點,就是不要在與 I/O 不相干的地方偷懶對 C++ iostream 使用繼承。
去年年中寫了一個簡單的小 database wrapper 程式庫,一樣是因為不方便使用四處可見的既有 library,所以得自己搞。裡面有一個 class 叫做 Statement,我希望它的用法就跟 cout 一樣,因為數字在 SQL 指令裡,一樣是以字串的形式呈現,另外,這樣也有機會可以依據傳入的型別的不同,自動決定要不要加 SQL 引號,以及要不要 escape 掉插入內容的引號。既然用法就像 cout 一樣,那就乾脆繼承自 ::std::ostream,然後一堆程式都不必寫了。
結果今天在用 Statement 的時候,要把一個 time_t 的值插入 SQL 裡,因為應該也必須,我有在程式一開始的時候設定好 locale 為正體中文,結果要插入的 1423912421 就變成了 "1,423,912,421",多了三個逗號,產生了錯誤的 SQL 指令。很明顯地,因為偷懶繼承 ::std::ostream 的關係,現在必須承受所帶來的 side effect 的苦果。
更糟糕的是,C++ 的 locale/iostream,大概是除了 STL 之外,另外一個 compiler vendor 很容易沒有做到 standard [...]
可惡,今天下午花了那麼久的時間,嘗試了無數的原因,就是找不到為什麼無法順利轉出正確的 UTF-8 資料來的原因。結果回家洗澡時才想到,出來確認一下,果然,我忘記 set locale 了。 我的青春啊,哭~ 所以,寫 main() 的時候,第一步千萬記得要 setlocale() 啊,真是血淚的教訓。
在 Standard C 裡頭,僅規定了我們可以利用 setlocale() 函式,來查詢或設定 C run-time 所用的,全域且唯一的 locale。然而,standard C 並沒有規定,傳進 setlocale() 的第二個參數,locale id,的格式與規定,而是交由各平台 (OS) 自訂。
所以問題來了,假若我們需要在程式裡 hard code 這個 locale id,以便傳給 setlocale(),那就會產生 portability 的問題。
解決辦法可以是,我們預先準備一張表格,訂定一套虛擬的 locale id 表格,在 compile-time 或 run-time 轉換成目標平台所用的 locale id。在 compile-time 我們可以利用 pre-processor 的 #define 來編寫這個表格,在 run-time 則建立 structure,linear-search 查表即可[1]。這樣子,我們就可以把 portability 問題,侷限在這個表格裡,且一併可達到 code-reuse 的目標。
因此,問題就轉換成了,我們怎樣建立與維護這個表格?這個表格不必太完整,但至少會用到的平台與 locale,都應該在裡面有資料。
今天就是碰到這個麻煩的問題。原本我僅為這個表,建立了正體中文,Windows、Linux/FreeBSD 的資料,因為大約也只會用到這麼多。更因為平常根本就是在正體中文裡頭跑,setlocale() 時直接就傳長度為零的字串 [...]
之前碰到一個問題就是,我們有一堆 UTF-8 的 XML 檔案,但實際上這些檔案應該都只包含中文字,也就是可以用 BIG5 呈現的字。然而,因為是 UTF-8 是 Unicode 的關係,所以實際上可能存在有無法轉換成 BIG5 的字,因而違反程式的期待,造成程式的問題。為了避免這樣子的問題,我們必須要檢查這堆 UTF-8 的 XML 檔案,確認每個檔案裡用到的每個 UTF-8 字元,都可以被轉換成 BIG5。這個問題,在考慮到其實 BIG5 也有很多變種的情況下,越是複雜。
不過還好,想通了話其實就很簡單:用 iconv 試轉不就得了:
SHELL> find . -type f -name '*.xml' \
| xargs -n 1 -t -x iconv -f UTF-8 -t CP950 \
[...]