發現了在XE4存在的問題 |
|
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
To ALL:
最近兩個月在轉移元件的時候,發現原本使用的String 資料類別轉為 AnsiString 類別時發生 了下列狀況(in Delphi XE4) Ex: var tempStr,str1:AnsiString; str2:AnsiString; li:integer; begin tempStr := '測試中文'; str1 := AnsiToUtf8(tempStr); //-->自此,轉出來的字串是UTF-8格式的資料,一樣是 '測試中文' 但是 li := Length(str1); //--> 此時,str1的資料正常的UTF-8字串轉眼變成一堆亂碼!! 當寫法換成 str2 := str1; li := Length(str2) ; //--> 此時,str2的UTF-8字串資料就不會被破壞變成亂碼 end; 這是個從XE系列之後就一直出現的現象,直到小弟發現了之後,多做了一道程序之後,問題才解決 不知道大家是否經歷過這樣的現象?? 小弟找這個問題找了足足兩個多月..... 原本以為是編碼的問題,但是看起來又不像是....... 編輯記錄
JL9168 重新編輯於 2013-07-06 23:17:13, 註解 無‧
|
P.D.
版主 ![]() ![]() ![]() ![]() ![]() ![]() 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
|
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
|
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
|
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
剛回到家,… 一看… (驚!) 上回我回的人就是樓主啊~~
所以你應該知道要改成UTF8String,而不能使用AnsiString啊~ http://delphi.ktop.com.tw/board.php?cid=30&fid=69&tid=104930 ===================引 用 aftcast 文 章=================== 沒來上我unicode祕技講座,否則就知道是為什麼。不過,你可以翻我不久前 回覆的文章有解(html方面)。但我沒深入回覆原理!目前我人在外面,打字很不方便。若回 去,有必要的話,再多回一點!
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
不過問題點真的不是使用UTF8String這個問題,而是在這個編譯環境中,只要寫程式的人,將資料倒入不應該
擅自變更原有資料的函數中,編譯器就應該保證參數資料應該是要絲毫無損的, 所以才會舉出 Length(str1) 這樣的例子,因為程式語言的資料內存是一個語言的根本,程式人員打心理相信 這樣的一個編譯環境,所以才能確切掌握每一行程式的環結是正常的,是可被信賴和控制的 以本例來說,牽連的程式碼有十萬行以上,將近五年累積的可靠程式區段,居然是被程式語言的內存自行修改資料 這樣的衝擊,個人會認為這個開發環境尚不成熟,或是沒有做好身為一個編譯器應該有的最基本要求。 這個感覺在BCB XE4 更是明顯,由編譯環境產生的*.hpp居然還有錯誤,include Package的編譯用到Designintf 與 DesignEditors 都還要自行加這個改那個的,感覺像是在替IDE 除錯,而不是在替程式除錯。 ===================引 用 aftcast 文 章=================== 剛回到家,… 一看… (驚!) 上回我回的人就是樓主啊~~ 所以你應該知道要改成UTF8String,而不能使用AnsiString啊~ http://delphi.ktop.com.tw/board.php?cid=30&fid=69&tid=104930 ===================引 用 aftcast 文 章=================== 沒來上我unicode祕技講座,否則就知道是為什麼。不過,你可以翻我不久前 回覆的文章有解(html方面)。但我沒深入回覆原理!目前我人在外面,打字很不方便。若回 去,有必要的話,再多回一點! |
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
如果不是因為要測試離線網頁資料,還真的找不到這樣的問題;原因就是因為直接輸出的檔案不需要用到
像是Length( )、Format( )這一類的字串處理函數,也許真的是因為要支援Unicode而有所修正,但是 既然程式人員的用意並不是要更動到資料,所以選用這些函式;然而卻因此變動到資料,比較正確的 方式不應該是這樣........即使用了ansistring ,只要在不應該會變動變數內資料的函數中,資料就不應當被變動。 倘若真的必須要全改為使用Utf8String.......天啊,那很難統計把舊程式或舊系統Mount上來要花多少時間成本......... 會不會最後變成......重寫比較快??!! 因為不知道下一顆地雷在那兒...... 除了這些之外,其餘的資料轉換就覺得比較理所當然,像是 var str1:AnsiString; ws:WideString; PWS:PWideChar; begin pws := PWideChar(str1); ---->這是錯誤的轉換......OK!! 這個道理可以接受 應該要改成 ws := str1; PWS := PWideChar(WS); 這些不同的編碼字串間的轉換,相形之下是合情合理的.....就不能算是Bug!!
編輯記錄
JL9168 重新編輯於 2013-07-07 23:12:46, 註解 無‧
|
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
|
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
嗯,我了解你說的情形與苦處! 但事實上是因為你還不夠了解新版的string方面的處理與架構。我可以肯定不是編譯器上的問題或錯誤,是因你"誤解"或說"誤用"了新的字串功能。當然,我也覺得embt在文件與書本上 (唉,又是老到不行的話…但無解) 並沒有很強的說明新字串的架構與用法,所以多數人還是不理解。
我的講座課堂上就有解說細節--如:新字串的結構。 我簡言之,新字串是一種「編碼感知」的字串。字串本身有一個「標示區」會記載它是什麼編碼,好比說是cp950(即big5) 或是 utf8 (cp65001)。 而為何又稱「感知」? 因為在你把 字串設定給字串時,它會「全自動的」去偵測編碼那個「標示區」,進一步的幫你做編碼轉換! 看似很貼心,但不明白的人會被搞死…因為不知何時被動了手腳… 不過,當你很明白這架構後,你就會覺得有貼心到。 以你所舉的例子來說,我肯你應該是有簡略了一些程式碼… 單純的執行到Length(str1)這一行後,str1的內容絕對還沒有任何變化。我把你的程式貼到xe3,經反組譯,加上翻cpu畫面裡的memory的二進位。肯定還是utf8的碼 (以你的這個例子)。 我想你說會出現亂碼應該是在那一行後面還有一些關於字串的show出,或是字串的再設給誰之類的… 再補充一些: 新版的AnsiString是不等於UTF8String的 (原因就是我剛講的「感知編碼」的關係,因為二者的標示區的值是不同的,所以算是不一樣的字串)。 str1,str4:AnsiString; str2,str3:UTF8String; str1 := '中文'; str2 :='中文"; str1在記憶體中是A4A4A4E5 str2則是 E4B8ADE69687 又,事實上你根本不需要用AnsiToUtf8() 這個函式。你只要: str3 := str1; //這行就搞定,why? 因為全自動感知而幫你轉碼 相反的 str4 := str3; //這時候的str4裡放的是A4A4A4E5, 又自動的被轉換了 所以,從此,big5轉utf8或utf8轉big5 (但這要小心,因為utf8是unicode,比如堃這個字,轉後會變?) 通通都是 一行搞定,只要 「你的字串宣告是正確的!」 而你的這一行: str1 := AnsiToUtf8(tempStr); //-->自此,轉出來的字串是UTF-8格式的資料,一樣是 '測試中文' 事實上是「錯誤的」寫法(在新版上),因為還是那句話,明明是utf8的字串,你硬塞給big5的字串。然而…為何str1真的還是放著utf8的編碼? 因為…那個函式傳回的是rawbytestring… 說到這裡,愈來會愈深入原理部份,但一時半刻難說清。但有幾個重要的概念可以給個hint : 「到底ansistring的定義是什麼? 為什麼這樣問? 因為以前utf8,明明就是用ansitring來放的啊…,而以剛你的例子來說,也真的放了utf8的內容… 而rawbytestring又是啥用的? 」 若不考慮深入的了解原理,那我覺得就是用 UTF8String 去接你的AnsiToUtf8函式就對了( 新版正解做法),「其實再強調一次,事實上這個函式在新版是不需要使用了,因為一行搞定」。 希望以上能有一點幫助。至於若你百分百肯定你的例子(就那些行數,沒有別的,也沒有省)到了lengh()那行就變… 我真的也很認真的想知道是否我真的錯過了什麼? 還請反饋讓我知道,感謝! ===================引 用 JL9168 文 章=================== To ALL: 最近兩個月在轉移元件的時候,發現原本使用的String 資料類別轉為 AnsiString 類別時發生 了下列狀況(in Delphi XE4) Ex: var tempStr,str1:AnsiString; str2:AnsiString; li:integer; begin tempStr := '測試中文'; str1 := AnsiToUtf8(tempStr); //-->自此,轉出來的字串是UTF-8格式的資料,一樣是 '測試中文' 但是 li := Length(str1); //--> 此時,str1的資料正常的UTF-8字串轉眼變成一堆亂碼!! 當寫法換成 str2 := str1; li := Length(str2) ; //--> 此時,str2的UTF-8字串資料就不會被破壞變成亂碼 end; 這是個從XE系列之後就一直出現的現象,直到小弟發現了之後,多做了一道程序之後,問題才解決 不知道大家是否經歷過這樣的現象?? 小弟找這個問題找了足足兩個多月..... 原本以為是編碼的問題,但是看起來又不像是.......
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
喔,剛想到一件重要的事沒補充:
若某函式的宣告長這樣 (容我用c 語法較熟) foo( String str) { String inStr = str; Lenght(inStr); } AnsiString ans = "中文"; foo(ans); 這時候 foo裡面的 inStr 已經是unicode,而不是原來的big5了… 原因? 一樣: 編碼感知 問題,全自動幫你(雞婆)轉了,若你是用 var 傳入(delphi裡的傳址) 你就更慘了。 又以你經常處理utf8的功能,想必經常遇到的應該是 bar (AnsiString ans) 但你把一個也叫 AsnsiString str1 (但事實上放的是utf8的內容,經ansiToUtf8函式而得),你會以為 ansistrin 的字串放入 ansistring的參數…一切不會變吧… 但,可能變了 (我說「可能」是因為我沒這樣用,我此刻也沒測)。若變是為什麼? 因為它str1這個參數一直真的被當「big5」來處理所有的工具函式等等的…但你卻傳給它的是一個 「偽」ansistring(記得ansistring是big5,新版的)。 以上補充
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
感謝aftcaft這麼深度的說明
>> 因為以前utf8,明明就是用ansitring來放的啊…,而以剛你的例子來說,也真的放了utf8的內容… 其實這才是重點所在,也因為考量相容性,一時之間沒有辦法把全部的程式都依照"新式" 的做法來處理, 所以"把有處理到UTF-8字串變數全都改為UTF8String" 這件事就變成一個遙不可及的想法,因為包袱太大了。 >> 你會以為 ansistrin 的字串放入 ansistring的參數…一切不會變吧… 實際上真的變了,以一個開發者的角度來說,也許認為這是個貼心的方式;但以一個使用者的角度來說 ,這樣的狀態是令人擔憂的(Delphi 給人的印象就是"Strong type"類型的程式語言)。 不過現在知道了,新的系統不會再用這類方式來寫了。 >>AnsiString ans = "中文"; >>foo(ans); >>這時候 foo裡面的 inStr 已經是unicode,而不是原來的big5了… >>原因? 一樣: 編碼感知 問題,全自動幫你(雞婆)轉了,若你是用 var 傳入(delphi裡的傳址) 你就更慘了。 感覺就是整個環境都改了,連舊的程式碼,內定的系統函式也不相容了........T_T........ 只有接受了........ 如果原來可用的函式已經不適用了,那還有哪些函式是不能這樣做或是需要重新檢視的呢?? 後來這件事情的處理方式就是,XE以後的版本自成一個系列;同一套套件以後要維護兩個版本。 但會不會隨之而來的問題就是....也許在新舊版本開發出來的套件中,編譯出來的DLL會有相容性的問題。 一旦有,那就表示之前的舊版模組環境都需要全面翻寫或是改寫,茲事體大.......... 還有幾個問題想請教 1.AnsiString類型應該不能定義為"專為處理big5字串"的變數吧?? 而是放置由ASCII Code組成的字串資料....對吧!! 2.如果在Delphi 中 Function() Result := '' ; 這一類的函式,是否也就不能以ansistring來定義了....對吧?? 那如果我們不確定傳回的字串資料是big5 ,gb2312 or 其他內碼.....那狀況就變得很棘手了.... 再次感謝您的回答 ===================引 用 aftcast 文 章=================== 喔,剛想到一件重要的事沒補充: 若某函式的宣告長這樣 (容我用c 語法較熟) foo( String str) { String inStr = str; Lenght(inStr); } AnsiString ans = "中文"; foo(ans); 這時候 foo裡面的 inStr 已經是unicode,而不是原來的big5了… 原因? 一樣: 編碼感知 問題,全自動幫你(雞婆)轉了,若你是用 var 傳入(delphi裡的傳址) 你就更慘了。 又以你經常處理utf8的功能,想必經常遇到的應該是 bar (AnsiString ans) 但你把一個也叫 AsnsiString str1 (但事實上放的是utf8的內容,經ansiToUtf8函式而得),你會以為 ansistrin 的字串放入 ansistring的參數…一切不會變吧… 但,可能變了 (我說「可能」是因為我沒這樣用,我此刻也沒測)。若變是為什麼? 因為它str1這個參數一直真的被當「big5」來處理所有的工具函式等等的…但你卻傳給它的是一個 「偽」ansistring(記得ansistring是big5,新版的)。 以上補充
編輯記錄
JL9168 重新編輯於 2013-07-08 11:09:11, 註解 無‧
|
ANDY8C
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
您的問題我也遇過了
原本用 DELPHI 2007 開發的軟體,一切順利,加上 TNT 的UNICODE 元件,也都順利 有一天,想替客戶增加真正的 UNICODE 版本,所以買了 XE2 ,從此日子就累了 我用的 3RD 元件不多,只有三個,大都是自己寫的, 我將 D2007 的程式,直接在 XE2 重新編譯....UI 的輸入及輸出都OK, 的確解決了 UNICODE 的問題,完全無痛....... 但是..... 通訊的功能,傳輸字串的功能,我就遇到很大的問題(標籤機的自行描繪字型/控制命令...等), 都是一些莫名其妙的問題,所以 D2007 改到 XE2 ,絕非 ANSI STRING 轉 WIDE STRING 的單純問題. 我都不太敢講,因為我對 XE2 不是很熟...... 出道早,所以累計太多小程式要改/檢查. 目前我的程式,也是一支一支的改(D2007 -> XE2) ;也為將來升級到 XE4 的問題準備, 說實在的,我是接案謀生,不是為研究 DELPHI 而用 DELPHI, 我不會用太高深的技巧來寫程式, 所以......不是也想了解是誰的問題,只要在客戶端運作正常就好. 以上,謝謝您 ===================引 用 JL9168 文 章=================== 還是要感謝aftcast 大大之前熱心回答小弟的問題,感激之至 只是對這樣的問題覺得有說出來讓大家參考的必要,若真的碰上了;能夠技巧性的繞過這樣的問題。 無非也是希望這個開發環境可以更可靠,畢竟還是希望用它的使用者能夠再多增加一些........ 至於Update1是否有針對這樣的問題........還不清楚.....可能要去找相關的資料了......
------
--------------------------------------- 偶爾才來 KTOP ,交流條碼問題,在 FB [條碼標籤達人] 社團留言,感恩.
編輯記錄
ANDY8C 重新編輯於 2013-07-08 14:03:19, 註解 無‧
|
ANDY8C
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
請問一下,您的問題是....字串內容還是字串長度有問題 ?
我剛用 XE2 測試 STR1,STR2 正常.....但 LENGTH 是 12,不知是否是您的問題 ?? 我寫的測試程式 http://delphi.ktop.com.tw/board.php?cid=31&fid=79&tid=105306 ===================引 用 JL9168 文 章=================== To ALL: 最近兩個月在轉移元件的時候,發現原本使用的String 資料類別轉為 AnsiString 類別時發生 了下列狀況(in Delphi XE4) Ex: var tempStr,str1:AnsiString; str2:AnsiString; li:integer; begin tempStr := '測試中文'; str1 := AnsiToUtf8(tempStr); //-->自此,轉出來的字串是UTF-8格式的資料,一樣是 '測試中文' 但是 li := Length(str1); //--> 此時,str1的資料正常的UTF-8字串轉眼變成一堆亂碼!! 當寫法換成 str2 := str1; li := Length(str2) ; //--> 此時,str2的UTF-8字串資料就不會被破壞變成亂碼 end; 這是個從XE系列之後就一直出現的現象,直到小弟發現了之後,多做了一道程序之後,問題才解決 不知道大家是否經歷過這樣的現象?? 小弟找這個問題找了足足兩個多月..... 原本以為是編碼的問題,但是看起來又不像是.......
------
--------------------------------------- 偶爾才來 KTOP ,交流條碼問題,在 FB [條碼標籤達人] 社團留言,感恩.
編輯記錄
ANDY8C 重新編輯於 2013-07-08 15:43:02, 註解 無‧
|
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
是字串的內容被修改了,內容是原本轉好的UTF-8字串
-->>> 因為以前utf8,明明就是用ansitring來放的啊…,而以剛你的例子來說,也真的放了utf8的內容… //--------------------------------------------------------------------------- <部分原始碼> Part1.----->Format( ) 出來的結果ResStr字串就由UTF-8 字串變為亂碼 ResSt r := Format('HTTP/1.0 200 OK'#13#10 'Content-Type: text/html'#13#10 'Content-Length: %d'#13#10 'Content:'#13#10#13#10'%s', [Length(ResStr), ResStr]); 後來這一段把Format( ) 拿掉,就正確了。 Part2 --> Length( ) 出來的結果ResStr字串又從UTF-8 字串變為亂碼 //回應給WebClient端的資訊!! CardiTemp := Length(ResStr); //--------------------------------------------------------------------------------------------------------- 大致上是這樣 所以如果要測試 Var str:AnsiString; len:integer; begin str := '測試字串'; <----這裡要放入UTF-8編碼的中文字,不是Big5編碼的字串 len := Length(str); end; ===================引 用 ANDY8C 文 章=================== 請問一下,您的問題是....字串內容還是字串長度有問題 ? 我剛用 XE2 測試 STR1,STR2 正常.....但 LENGTH 是 12,不知是否是您的問題 ?? 我寫的測試程式 http://delphi.ktop.com.tw/board.php?cid=31&fid=79&tid=105306 ===================引 用 JL9168 文 章=================== To ALL: 最近兩個月在轉移元件的時候,發現原本使用的String 資料類別轉為 AnsiString 類別時發生 了下列狀況(in Delphi XE4) Ex: var tempStr,str1:AnsiString; str2:AnsiString; li:integer; begin tempStr := '測試中文'; str1 := AnsiToUtf8(tempStr); //-->自此,轉出來的字串是UTF-8格式的資料,一樣是 '測試中文' 但是 li := Length(str1); //--> 此時,str1的資料正常的UTF-8字串轉眼變成一堆亂碼!! 當寫法換成 str2 := str1; li := Length(str2) ; //--> 此時,str2的UTF-8字串資料就不會被破壞變成亂碼 end; 這是個從XE系列之後就一直出現的現象,直到小弟發現了之後,多做了一道程序之後,問題才解決 不知道大家是否經歷過這樣的現象?? 小弟找這個問題找了足足兩個多月..... 原本以為是編碼的問題,但是看起來又不像是....... |
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
>>如果原來可用的函式已經不適用了,那還有哪些函式是不能這樣做或是需要重新檢視的呢?? >>後來這件事情的處理方式就是,XE以後的版本自成一個系列;同一套套件以後要維護兩個版本。 如果以utf8的問題來說,建議還是將舊碼改成utf8string,因為舊碼會自動把這個型別轉成ansistring (我想這是當年留下的伏筆)。而同一份碼若到新版編譯,則當然就不會如剛講的被轉成ansistring,而是真的utf8string這個型別! 於是可以相容。 然而,事實上問題可能不會簡單到只要換型別,因為你可能寫了一堆的自己函式,而其中的參數是宣告成ansistring,但傳入的是「經ansistoutf8所得的anistring(在新版已不能再稱anistring)」,於是你要把參數也換成utf8string。舉例,你有個函式 foo(ansistring str) //你心中了解將要傳入的是utf8的字串,而不是big5的字串。那麼你就要改成 foo(utf8string str) //改成這樣後,新舊也都合。 >>但會不會隨之而來的問題就是....也許在新舊版本開發出來的套件中,編譯出來的DLL會有相容性的問題。 不會! 不同編譯器編出來的dll是封閉的。但你心中依舊要一直記得,若是舊的dll,裡面有ansistring參數,但事實上是要被傳入utf8的資料,那麼你在新版程式中,要呼叫舊的dll時,你自己要先把新的那個字串搞成utf8string後,再呼叫你舊dll裡的那個函式。 還有幾個問題想請教 >>1.AnsiString類型應該不能定義為"專為處理big5字串"的變數吧?? 而是放置由ASCII Code組成的字串資料....對吧!! 對,但更精準的講是…ansistring是專門處理1個字元(1byte)為基礎的字串。也因此,這個特質在以前也可以處理utf8的內容。然而,新版的字串加入了「編碼感知」的功能與標示後,它就不再單純的僅僅處裡一個字元為基礎,而是會動態的去幫你轉碼。舊版的字串沒有那個標示區,也就是說僅存資料(不管你是big5或是utf8或是gb2312…反正都是以單字元為單位 (或許你會想…中文不是二個bytes嗎?…事實上,編譯器是當它一個一個的…。big5在英文字時是不是單一? 是。變中文時是不是變2,是。那和utf8在英文是1,在中文多數是3,是一樣的)。 >>2.如果在Delphi 中 Function() Result := '' ; 這一類的函式,是否也就不能以ansistring來定義了....對吧?? >> 那如果我們不確定傳回的字串資料是big5 ,gb2312 or 其他內碼.....那狀況就變得很棘手了.... 嗯,你說的對。所以會有一個叫rawbytestring 的東西,就是為了解你說的這個情形。配合有個查目前string編碼的函式,可以解到底是回傳了什麼。 我給個例子: procedure TForm9.FormActivate(Sender: TObject); var str:AnsiString; rb:RawByteString; i: integer; begin str := '中文'; rb := str; i := StringCodePage(rb); end;
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
不好意思,再多請教一些問題
在尚未發現這個問題時,原本我們以為這樣的情形是因為WinSocket API 或是相關的 API 在XE以後的定義 都因為資料型態的改變而改變了 例如: 原本為PChar( )的參數,都被改成PWideChar( ) 所以才會有這個問題;後來測試過之後才發現 根本不是這樣!! 但是也因此發現了這樣的資料轉換邏輯 var str1:AnsiString; ws :WideString; Pws:PWideChar; Pws := PWideChar(str1); --->這樣似乎資料也會出錯 必須寫成這樣 ws := str1; Pws := PWideChar(ws); 可以請你說說為何第一個程式為何也不正確呢?? (可是這是可以通過編譯的!!) 另外,有沒有可能有一種情形是 var str1:Ansistring; str1 := 'Big5字串' 'GB2312字串'; <<----這樣的情形會變成如何? 謝謝你撥冗回答問題 |
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
事實上,經過剛一直玩,發現其實在正常的情形下要讓問題變錯很不容易。舉例說,若用ansitoutf8這個函式,轉出來的結果會把原字串的標示區一並修改成utf8的編碼(即使型別上還是你用了ansistring,而非utf8string,但此刻的那個字串已經側底的是「標示是utf8 」, 「內容也是utf8」了,所 以再經轉來轉去,感覺也不太會出錯。
我認為,會出問題,最主要都是因為「在不改標示(事實上你不能手動改,除非你透過函式的傳回值)的前提下,你去修改了該字串的內容為utf8的內容」,這樣的結果會是「頭(標示區)記的是big5,而你身體(內容),卻是utf8。 進一步若在有被換碼的時候,問題就出來,比如說你叫showmessage時會轉一次變成unicode。 底下是我的測試範例: procedure TForm9.FormActivate(Sender: TObject); var str,str2,str3:AnsiString; rb:RawByteString; i: integer; u8, uu8:UTF8String; u16:string; begin rb := #$E4#$B8#$AD#$E6#$96#$87; // 中文的utf8的二進位,硬塞 //u8 := #$E4#$B8#$AD#$E6#$96#$87; 這行很有趣,放到u8裡的值已不是你給的,感知呀~~~ str := #$E4#$B8#$AD#$E6#$96#$87; // 這時候的str是掛羊頭賣狗肉的。頭是big5的記錄,身體卻放著utf8的編碼 (以新版的字串觀點) u8 := rb; // 這是頭與身體都是合的正確字串 //str := rb; Length(str); // 事實上length是絕對不可能改變任何的字串內容的,它只是把字串裡的「長度」讀出來,你mark這行是一樣的結果 str2 := 'CDE'; str2 := str2 str; // str2 現在是頭是big5,身體是混種,裡面有big5的cde,也有utf8編碼的「中文」 uu8 := 'ABC'; uu8 := uu8 u8; ShowMessage(str2); // 這裡轉一次unicode,於是出錯 ShowMessage(uu8); // uu8是正確的,所以再轉unicode也是不會錯! rb := str; i := StringCodePage(rb); str3 := AnsiToUTF8(str); end; 以上的範例極重要,若可以清楚的知道就一定會內力變強。當然,可能要配合會在cpu的debug畫面上去找出字串的所在記憶體二進位的地方,希望上回上我課的學員還會記得怎麼處理! ===================引 用 JL9168 文 章=================== 是字串的內容被修改了,內容是原本轉好的UTF-8字串 -->>> 因為以前utf8,明明就是用ansitring來放的啊…,而以剛你的例子來說,也真的放了utf8的內容… //--------------------------------------------------------------------------- <部分原始碼> Part1.----->Format( ) 出來的結果ResStr字串就由UTF-8 字串變為亂碼 ResSt r := Format('HTTP/1.0 200 OK'#13#10 'Content-Type: text/html'#13#10 'Content-Length: %d'#13#10 'Content:'#13#10#13#10'%s', [Length(ResStr), ResStr]); 後來這一段把Format( ) 拿掉,就正確了。 Part2 --> Length( ) 出來的結果ResStr字串又從UTF-8 字串變為亂碼 //回應給WebClient端的資訊!! CardiTemp := Length(ResStr); //--------------------------------------------------------------------------------------------------------- 大致上是這樣 所以如果要測試 Var str:AnsiString; len:integer; begin str := '測試字串'; <----這裡要放入UTF-8編碼的中文字,不是Big5編碼的字串 len := Length(str); end;
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
===================引 用 JL9168 文 章=================== 不好意思,再多請教一些問題 在尚未發現這個問題時,原本我們以為這樣的情形是因為WinSocket API 或是相關的 API 在XE以後的定義 都因為資料型態的改變而改變了 例如: 原本為PChar( )的參數,都被改成PWideChar( ) 所以才會有這個問題;後來測試過之後才發現 根本不是這樣!! 但是也因此發現了這樣的資料轉換邏輯 var str1:AnsiString; ws :WideString; Pws:PWideChar; >>>>>>>>>> Pws := PWideChar(str1); --->這樣似乎資料也會出錯 次。若你用了PAnsiChar移二次,剛好到0x43,嗯,正確!!! 若你是PWideChar,被移了二次,就爆爆爆點了…跑到C這個字元外的不明內容上! 若你先把AnsiString改成WideString,那麼你的記憶體變成 0x41 0x00 0x42 0x00 0x43 0x00 (別問我為何有0在中間…因為它是unicode啊)。因此你變成了6個bytes,那麼你用PWideChar來移動,耶……移動一次,剛好在 0x42,移動第二次,剛好0x43… 真是合的。 但以上的說明都是在英數的情形下,若是中文字,就沒那麼剛好,因為中間不會是 0x00 補在中間啦! 必須寫成這樣 ws := str1; Pws := PWideChar(ws); 可以請你說說為何第一個程式為何也不正確呢?? (可是這是可以通過編譯的!!) 另外,有沒有可能有一種情形是 var str1:Ansistring; >>>>>>>>>>>>>>>>>>>str1 := 'Big5字串' 'GB2312字串'; <<----這樣的情形會變成如何? 常數」字串動任何的手腳,直就「接上去」
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
aftcast
站務副站長 ![]() ![]() ![]() ![]() ![]() 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
再補充上面講的 pwidechar 指標等相關問題。
一般來說,變指標有二種可能需求, 一種是要被 c 語言之api (包含windows sdk api),直接叫用裡面的字串內容。 第二種,是會被再拿來搬移,修改,補上一些字用 : 這種情形下就會考量到我剛講的移動問題。 以ansistring要給function當參數用時… 若function要求ansistring的指標,理所當然你一定要給pansichar,一方面是一次動一個byte,二方面該字串的編碼是對方要的(如big5(含英數))。 若對方要的是 pwidechar 的參數,那你要先轉成widestring是應該的,因為編碼它要求是unicode,其次,也可能它會在裡面一次搬二個byte來做一些memory的處理。
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
JL9168
中階會員 ![]() ![]() ![]() 發表:133 回覆:223 積分:76 註冊:2011-09-29 發送簡訊給我 |
OK ,現在清楚的知道原因了;其實這些自己發展的函數之中,多了一個自轉UTF-8的函式 >>AnisStrToUtf8( )
當初的定義是把它從D7的基礎單元分離出來,用來完成D7以下版本的UTF-8轉移公用函式,所以定義上如下 function AnisStrToUtf8(inStr:AnsiString):AnsiString; 轉到XE系列之後,雖然轉出來的資料是正確的UTF-8字串,但是在XE環境就會被變動了!! 所以用塞UTF-8到AnsiString的做法基本上是符合這個情境的。 為何不用新的做法,道理也很簡單,因為每一次輸出網頁資料不一定都是使用UTF-8編碼,套件中是交給 系統開發者來決定,所以不能預設都是用UTF8String..... 然而在XE系列的ansiToUtf8( ) 函數中, 它的輸出有可能會是空字串(這說來話長,因為要做Client端 Request 解析) , 若不用自己的函式,系統會出錯。 OK,其實我們的觀點是希望原廠在規劃這些實作時,能更有彈性一些,比方說,考量到一些舊有包袱的移轉, 這類的函數能有個Flag變數,讓設計人員可以自行決定;要不要自動轉換,而不是直接就自行轉換,立意雖好 但也造成困擾,因為沒有完全完美的方案,但希望至少程式人員有選擇如何處理的機會(不然,像有些與機器 的溝通有些與KeyPro的溝通,有些與舊式RS232設備的溝通,過程中資料被修改;那可是一件恐怖的事情)。 畢竟開發工具的投資是長長久久....大家的包袱只有越來越大.... 感謝大大們的參與與回應,謝謝你們無私的付出;也希望大家再看這篇論述的過程中,得到一些想知道的知識。 以上 |
Main Chen
高階會員 ![]() ![]() ![]() ![]() 發表:29 回覆:135 積分:127 註冊:2002-10-07 發送簡訊給我 |
|
ANDY8C
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
您的想法我以前也提過,但這些大廠應該不會聽的......官大學問大 我以前曾提,STRING 宣告,不論到哪個版本,維持不變,都是 AnsiString (維持 D7 , D2007 那種舊格式) 但新版(XE,XE2,.....等) STRING 可以改用 XE_STRING 之類的宣告,才會變成 WideStribg 的格式 或者直接宣告 WideString 才是 Wide 功能, 這樣舊程式不就不用改了嗎?? ===================引 用 JL9168 文 章=================== 為何不用新的做法,道理也很簡單,因為每一次輸出網頁資料不一定都是使用UTF-8編碼,套件中是交給 系統開發者來決定,所以不能預設都是用UTF8String..... 然而在XE系列的ansiToUtf8( ) 函數中, 它的輸出有可能會是空字串(這說來話長,因為要做Client端 Request 解析) , 若不用自己的函式,系統會出錯。 OK,其實我們的觀點是希望原廠在規劃這些實作時,能更有彈性一些,比方說,考量到一些舊有包袱的移轉, 這類的函數能有個Flag變數,讓設計人員可以自行決定;要不要自動轉換,而不是直接就自行轉換,立意雖好 但也造成困擾,因為沒有完全完美的方案,但希望至少程式人員有選擇如何處理的機會(不然,像有些與機器 的溝通有些與KeyPro的溝通,有些與舊式RS232設備的溝通,過程中資料被修改;那可是一件恐怖的事情)。 畢竟開發工具的投資是長長久久....大家的包袱只有越來越大.... 感謝大大們的參與與回應,謝謝你們無私的付出;也希望大家再看這篇論述的過程中,得到一些想知道的知識。 以上
------
--------------------------------------- 偶爾才來 KTOP ,交流條碼問題,在 FB [條碼標籤達人] 社團留言,感恩. |
GrandRURU
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:240 回覆:1680 積分:1874 註冊:2005-06-21 發送簡訊給我 |
原廠表示:請用FireMonkey Framework,上述問題絕不復見!
===================引 用 ANDY8C 文 章=================== 您的想法我以前也提過,但這些大廠應該不會聽的......官大學問大 我以前曾提,STRING 宣告,不論到哪個版本,維持不變,都是 AnsiString (維持 D7 , D2007 那種舊格式) 但新版(XE,XE2,.....等) STRING 可以改用 XE_STRING 之類的宣告,才會變成 WideStribg 的格式 或者直接宣告 WideString 才是 Wide 功能, 這樣舊程式不就不用改了嗎?? |
ANDY8C
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
|
leveon
資深會員 ![]() ![]() ![]() ![]() ![]() 發表:30 回覆:389 積分:303 註冊:2012-02-12 發送簡訊給我 |
這一篇很棒 讓人更加瞭解Delphi對Unicode支援的處理
之前剛試玩XE的時候 就發現string會自己轉碼 就猜想 string的結構上應該有類似codepage的東西 轉碼的實作應該也是利用作業系統的API去轉 個人認為目前新版Delphi 對string的處理是很合理的 舊Delphi應該就有想過要支援unicode 所以到處留下伏筆 如果再多一個型別出來 現有的VCL要全面支援unicode 會變的很困難 比較有問題的是在舊版Delphi寫Unicode程式的 轉到新Delphi可能會比較麻煩 RawByteString預設不填codepage 可以幫助debug 寫3rd的要支援新舊Delphi 可能要用編譯指令指示編譯 器編譯不同區段的Code會比較適當 至於上面說的firemonkey 還是IOS的支援 架構上 我認為Delphi做的不好 走錯了第一步 不過在這裡講這個可能會被攻堅 所以不討論了 顆顆 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |