pretty code

2020年11月20日 星期五

讀取書名遇到的 Bug

最近在轉檔時,遇到某本書名使用程式無法解析,程式會判斷沒有書名資訊,但用文字編輯器卻看不出有什麼問題?

該檔案是一個 UTF-8 編碼的 XML 檔,但我自己是習慣使用 Re 來解決這類小問題,我的正規表示法如下:

let regex = /dc:title.*>(.*)<\/dc:/

不論是 Javascript 或是 Python,都可以在正規表示式中任意圈出自己想要的資料,之後拿來使用就很方便。

回到問題身上,一開始會以為是不是書名裡有特殊字元導致,尤其這本書的書名接近 60 個字,實在嚇人。但把檔案和程式簡化後繼續測試,卻發生了隨機事件,有時候可以成功,有時候仍然不行?

嘗試使用 grep 和 Python 程式,兩者都可以正常執行,難道是我用的 Node.js 版本太舊導致?寫程式的大概都聽過類似的笑話:我的程式看起來沒問題呀,一定是 Compiler 的 Bug!殊不知這個是最不可能的事,如我一般的普通人寫的程式,理論上撞到這種問題的機率是非常的低,所以這個念頭也只是一閃而過。

好吧,來看看 binary 值好了,只要不是 ASCII 編碼的檔案我都很懶得看,畢竟我也無法一眼看出該中文的編碼為何?為了驗證這個問題,勢必要找出相關編碼的值才行。

檔案的 binary 內容長這樣。

書名最後 4 個字的編碼長這樣。

相比之下,結尾多了一個 E2 80 A9 的字,拿掉它之後,我的程式就恢復正常了。後來回想,之前發生隨機事件時,其實 UltraEdit 已經有徵兆,有時候針對該段文字反白複製時,偶爾便會看到一個不存在的字元,只是當時沒有想太多。

雖然問題解決了,但回到問題的本身,這個字是不合法的 UTF-8 字元嗎?

我們先來看看它的 binary 值。

1110 0010 # 1000 0000 # 1010 1001

它屬於 3 個位元的字元,其個別位元的開頭也有符合規則。將剩下的黑字再轉回 Unicode 碼,其值為 \u2029,跟用 Python Code 的 Re 結果一致。

使用 Chrome 直接開啟 XML 檔,可以正常顯示出該字元,使用全字庫網頁查詢,該字是一個中日韓相容表意文字區編碼。

查到這裡我就真的沒輒了,要再細究下去,可能要往 Node.js 用的 Re Code 方向去追查了。

後記

快下班時,無聊使用 \u2029 關鍵字查詢,才發現原來 \u2029 或是 \u2028 等字元,在正規式中是當做 a single white space,故會造成正規引擎把這一字元視為換行,而 "." 正規字元不包含換行字元,雖然可以用 "s" flag 來 match 換行字元,但書名就會包含換行字元,解決方式也很簡單,稍微更改 Re 即可。

let regex = /<dc:title.*>(.*)(\s*)<\/dc:title/


2020/11/22 更新

\u2028 是行分隔符

\u2029 則是段分隔符

不過重點還是 "." 字元不包含換行字元以外的字元, 我一直以為是包含全部字元,也算是學到一課了。

另外,這本書還真的是問題多多,放進我的 PW3 沒辦法更改字型,我目前的 PW3 版本為 5.13.2,我一向都是用 kindlegen 轉檔,這樣直式的書才能維持直式。

針對這個問題,好幾個網站都說即使是使用新型的 MOBI 格式,目前也不支持更改字型了,一定要使用 AW3 格式。但我覺得應該不是?一來我只有偶爾幾本書不行,二來我也有用 calibre 轉成 AW3 格式,結果一樣是不能更改字型。還好後來有看到一篇文章,說是把 CSS 裡面指定的字型拿掉即可,也就是 font-family 這行敘述拿掉,說也奇怪,這本書就這樣恢復正常了,我也終於可以使用我最喜歡的圓體字型來閱讀。

我後來想想,這個只是建議字型,理論上應該不影響功能才對。我目前只能想到 2 個原因,1 個是因為指定的字型有問題導致 Kindle 有 Bug,另外 1 個就是這本書不知道哪裡違反了 EPUB Spec 的規範。

我現在只能確定,隨便找了一本可以換字型的書,其 CSS 檔案也是有指定字型,差別在這本書的字型名稱沒有使用雙引號包住,而有問題的書則是同一行中有使用單引號,也有使用雙引號。理論上單雙引號皆可,只有在字型名稱包含空白時,才會使用引號包住。

沒有留言: