a problem Read & ReadEx in Powerbuilder 10

Powerbuilder 10 (含)以後的版本,檔案的讀取分為 Read 與 ReadEx 兩個指令,按照說明文件的用法 ReadEx 支援 StreamMode! 讀取每次可以超過 32767 個位元組,並且支援 TextMode! 的讀取模式,算是很好用的;但是這次的問題卻出在使用 LineMode! 開啟檔案 時 PB10 的 Read 這個指令上:

先看看這段程式碼哪裡出了問題,我有純文字一份文件 A.txt (ANSI編碼)內容為:
數據窗口揭秘:未公開的數據窗口事件 (作者:Mark Brown) 到目前為止,PB的數據窗口控件仍是PB眾多控件中功能最強大,最複雜的控件。 數據窗口固有的行為
然後利用下面這段程式碼讀取內容並顯示在 mle_1(MultiLine Editor)上面
integer li_fn string ls_str mle_1.text = "" li_fn = FileOpen("A.txt",LineMode!,Read!) if li_fn > 0 then //逐行讀取並顯示在mle_1上面 do while FileRead(li_fn , ls_str) >= 0 mle_1.text += ls_str +"~r~n" loop FileClose(li_fn) end if
mle_1物件上的顯示內容:
數據窗口揭秘:未公開的數據窗口事件 的數據窗口事件 事件 (作者:Mark Brown) ) 到目前為止,PB的數據窗口控件仍是PB眾多控件中功能最強大,最複雜的控件。 件中功能最強大,最複雜的控件。 複雜的控件。 鞳C 數據窗口固有的行為 的行為
發現了嗎?讀取出的內容怪怪的,幾乎每一行後段文字都重複被讀出,導致整個內容大亂。
但是,這段程式碼如果放在 PB6、PB7、PB8、PB9 執行,卻一點問題就沒有。

整個詭異到不行。

但是如果是英數文內容的檔案,不管哪個版本 PB6 or PB10 就完全不會有問題,測試檔案內容如下:

calculate the height of the element and divides that by two to center it the value is inversed so as to pull the window above the target the offset of 12 is added to account for the height of the field form element
後來,我把 Read 改成 ReadEx 指令後,在進行一次測試,發現無此問題:

integer li_fn string ls_str mle_1.text = "" li_fn = FileOpen("A.txt",LineMode!,Read!) if li_fn > 0 then //逐行讀取並顯示在mle_1上面 do while FileReadEx(li_fn , ls_str) >= 0 mle_1.text += ls_str +"~r~n" loop FileClose(li_fn) end if
看起來像是中文字造成衝碼問題,但仔細檢查被中斷文字前後都沒有所謂衝碼字元,以前PB6會有蚯蚓符號『~』的衝碼問題,PB10以後應該不會有此問題啊?

後來我注意到,讀出的內容有種巡迴式的重覆中斷現象,不像單純字碼衝碼的現象,且 ReadEx 指令是PB10以後的版本第一次出現的新指令,而 PB10 的特性是支援 Unicode ,所以就假設會不會是字元長度計算錯誤,由其比較明顯的是在 ANSI 編碼文字下,中文是 2Byte (半形)英數字僅 1Byte。
所以就用此假設試算一下 A.txt 的第一行:
數據窗口揭秘:未公開的數據窗口事件
此段文字不包含換換行字元共 34Byte 採用 ANSI 計算長度就是 34 ,
若採 unicode 算法(一個中文只會當成一個字)其實只得到長度 17,
假設 PB10 底層計算採用了 unicode 方式計算了該字串的長度 17 加上換行 2 個字元,
共 19 個字元
由於檔案是ANSI編碼,因此當 PB10 進行 File Seek 定位時會以 Byte 為單位計算到下一行起始字元的動作時,就會定位到該行『的』這個字,所以下一次 FileRead 時就會變成讀到『的數據窗口事件』這段文字。

得到以上結論以後,我更大膽架設,如果 A.txt 是 Unicode 編碼的話就不會有此現象,所以將 A.txt 用記事本開啟以後,另存新檔,並將編碼方式改成 Unicode ,重新以第一段程式碼讀取。

果然就不會出現此現象了。

為什麼純英數文字檔案不會有此問題? 那是因為純英數文字在ANSI編碼下一個字就是長度 1,而 Unicode 下也是一個字長度也是 1,這就是為什麼不會錯的問題。

尤其 PB10 以後的版本是以 unicode 為主體,所以長度計算指令 Lan(stringblob)算出來的是 unicode 長度,若要計算 ANSI 長度就要使用 LanA(Stringblob) 這個指令。

所以,在  PB10 的 Help 中關於 FileOpen 的說明有一段耐人尋味的描述:
FileRead and FileWrite have limitations on the amount of data that can be read or written and are maintained for backward compatibility.
也就是說為了保持相容性,對這些舊指令加了一些限制,也畢竟是外國人設計的軟體,通常開發者對其他雙字元集語系國家文字就可能沒有發現此類問題,否則就要看看Sybase是否還有出 EBF 可以修正這問題了。

在還沒有 EBF 可以修正以前,改用 ReadEx 指令會是比較正確的作法。

留言

這個網誌中的熱門文章

【研究】列印的條碼為什麼很難刷(掃描)

C# 使用 Process.Start 執行外部程式

統一發票列印小程式