slimxml 編碼錯誤排查
緣起
前一段時間,同事遇到一個配置文件讀取錯誤問題,讓我?guī)兔纯?。最開始不相信這么基礎(chǔ)的功能會有問題。排查完發(fā)現(xiàn),確實是一個字符編碼轉(zhuǎn)換方面的 bug,而且只有特定字符會有問題。一起來看看吧。
簡化問題
為了使問題簡化,我把配置文件內(nèi)容改成了下面這樣,只有一條記錄。

嘗試重現(xiàn)
網(wǎng)上找到 slimxml 源碼,編譯,運行。
說明: 這個
xml庫是非常古老的一個庫了。不建議使用。
加載上面的 xml。查看結(jié)果,果然與預(yù)期的不一致。配置文件里的字符是 φ,用 utf8 編碼存儲的。對應(yīng)的字節(jié)流是 cf 86。
加載到內(nèi)存中后,使用 unicode 存儲的,對應(yīng)的字節(jié)流是 cf 00。

而 φ 在 utf16 編碼下對應(yīng)的字節(jié)流應(yīng)該是 c6 03 (小端字節(jié)序)。

編碼轉(zhuǎn)換的bug?
配置文件里是使用 utf8 保存的,加載到內(nèi)存中后變成了 unicode 的,加載過程中一定發(fā)生了編碼轉(zhuǎn)換。難道是轉(zhuǎn)換過程中發(fā)生了錯誤?單步跟蹤 slimxml 加載文件的過程。很快就找到了關(guān)鍵函數(shù)—— utf8toutf16()。簡單跟蹤后,發(fā)現(xiàn)下面的處理代碼有些問題,
else if ((*u8 & 0xe0) == 0xc0)
{
if (size < 2)
{
break;
}
*(u16++) = (*u8 & 0x1f) | ((*(u8+1) & 0x3f) << 5); // <-- something wrong
u8 += 2;
size -= 2;
++converted;
}
于是,網(wǎng)上找了一段 utf8 轉(zhuǎn) utf16 的代碼,替換上面的處理,替換后的代碼如下:
else if ((*u8 & 0xe0) == 0xc0)
{
if (size < 2)
{
break;
}
*u16 = (*u8 & 0x1F) << 6;
*u16 |= (*(u8 + 1) & 0x3F);
u16++;
u8 += 2;
size -= 2;
++converted;
}
再次運行,發(fā)現(xiàn)效果正常。

總結(jié)
本次排查非常順利,一是因為目標(biāo)比較明確,二是因為在整個排查過程中,我清楚的知道每一步的正確結(jié)果是什么。字符編碼是每個程序員的基本功,一定要熟練掌握。本文的參考資料中包含了非常給力的在線編碼轉(zhuǎn)換工具,非常值得收藏。
參考資料
https://www.tamasoft.co.jp/en/general-info/unicode.html
https://code.google.com/archive/p/slimxml/downloads
https://onlineunicodetools.com/convert-unicode-to-utf8
https://onlineunicodetools.com/convert-unicode-to-utf16
https://www.browserling.com/tools/utf8-encode
https://www.browserling.com/tools/utf8-decode
https://gist.github.com/rechardchen/3321830
