注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

猫工的博客

无限风光在险峰

 
 
 

日志

 
 

Lazarus 对于 String 编码的 BUG 与处理  

2011-02-17 11:33:41|  分类: Lazarus |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

转自:www.fpccn.com lazarus中文社区

 

分類 : win32 版
 
Lazarus 0.9.24 以前的版本中,  String 是 ANSI 編碼,一個英文是 1 Byte,一個中文字是 2 Byte。Lazarus 0.9.26 版以後, 開始使用 UTF8 編碼,英文字母佔據的空間不變,中文字佔據的空間將是 3~6 個 Byte 不等。但在某些場合中我捫需要精確計算字串位置與長度, 如發票印表機自己有帶字型, 根據 ANSI STRING "一個英文是 1 Byte,漢字是 2 Byte" 的規則及使用習慣, 傳統上 BCB 5/6 或 Delphi 7 把中文字的 String (ANSI CODE) 丟到 ComPort 即可列印, 但 Lazarus 0.9.28 的 String 是 UTF8 CODE , 若是直接將中文的 String ( UTF8 CODE ) 丟到 ComPort , 會讓發票印表機印出亂碼, 故要特別處理 UTF8 -> ANSI 的 String 轉換
 
Lazarus 0.9.28 在 LCLProc單元中提供了幾個與 UTF8 字串相關的函數,例如有 Utf8ToAnsi(), AnsiToUtf8,  UTF8Length()、UTF8CharAt() 等等,在一定程度上可以給我們提供幫助 , 我自己也寫了一些公用函式, 來加強 UTF8 與 ANSI 字串編碼的處理
 
  BUG  (以下都是在 Lazarus 0.9.28 之 WIN32 版本下測試發現的, 其它平台如 Linux 版不一定會有相同狀況)
雖然  Lazarus 0.9.28  已全面使用 UTF8 編碼, 但仍有些函式取回的 String 仍為 ANSI String , 混合處理的情況下會造成許多困擾, 所以在此整理一下
 
▲ INI FILE
 //TIniFile 物件字串的 "讀出值" 及 "讀出預設值" 仍為 ansi, 故要轉換
MyIni:TIniFile;
procedure TForm1.FormCreate(Sender: TObject);
begin
  MyIni := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
  MyPath := ExtractFilePath(Application.ExeName);
  MyPath:=ansitoutf8(MyPath); //改為 UTF8
  MyList := TStringList.Create;
  MyList.Clear;
  tmp:=MyPath+'POSDB.FDB'; //路徑可能夾帶中文
  FDB_FN:=ansitoutf8(MyIni.ReadString('PARAMETER', 'FDB_FILENAME', utf8toansi(tmp)));
  ShowMessage(FDB_FN);
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  MyIni.WriteString('PARAMETER', 'FDB_FILENAME', utf8toansi(FDB_FN));
  MyList.Free;
  MyIni.Free;
end; 
 
▲ ExtractFilePath() 函式 : 取出某檔案的所在路徑
我發現它取出路徑的字串仍是 ANSI String 編碼, 要與一般 String 變數 (default 是 UTF8 編碼) 做 "+" 號處理會有問題, 所以每次取出後要用 AnsiToUtf8() 轉成 UTF8 String
 
▲ FileExists() 函式 : 判斷檔案存不存在
if (FileExists(Utf8ToAnsi(SDB_FN))) then
 begin
    .....
 end;                                   
 
▲ TStringList 物件的主要屬性仍為 Ansi String
//以下為正確處理示範
procedure TForm1.Button1Click(Sender: TObject);
var TmpList: TStringList;
    i:integer;
begin
  TmpList := TStringList.Create;
  TmpList.LoadFromFile(Utf8ToAnsi('C:\新資料夾\新增文字文件.txt'));
  for i:=0 to TmpList.Count-1 do
  begin
    ShowMessage(AnsiToUtf8(TmpList.Strings[i]));
  end;
  TmpList.SaveToFile(Utf8ToAnsi('C:\新資料夾\新增文字文件2.txt'));
  TmpList.Free;
end;        
 
▲ 某些控件 String 屬性需為 Ansi String
如 要設定資料庫控件 TDbf 的 TableName 屬性, 需傳入 Ansi String
My_Path:=ExtractFilePath(Application.ExeName);  //ExtractFilePath() 取回的是 ANSI
Dbf1.TableName:=My_Path+'myTest.DBF';  //TDbf 的 TableName 可直接餵入 ANSI STRING
而指定另一種資料庫控件 SQLite3Connection1 的 DatabaseName 屬性 卻又須使用標準 UTF8 String
My_Path:=ExtractFilePath(Application.ExeName);  //ExtractFilePath() 取回的是 ANSI
My_Path:=AnsiToUtf8(My_Path); //改為 UTF8
SQLite3Connection1.DatabaseName:=My_Path+'myTest.db';  //指定一個 DATABASE 要餵入 UTF8 STRING
所以以後若指定某控件的字串屬性, 明明是正確的字串內容, 但卻出現編譯錯誤或是控件無法正確動作, 可能就是 String 編碼問題
 
 自己寫的公用函式
//---------------------------------------------------------------------------
//Length(), Copy(), Pos() 傳入 Ansi String 才會準確
//UTF8Length(), UTF8Copy() 傳入 UTF8 String 才會準確
//---------------------------------------------------------------------------
//傳入傳出都以 UTF8 字串為準
function _Length(str: String):Integer; //或稱 AnsiLength()
var tmp:String;
begin
 tmp:=UTF8ToAnsi(str);
 result:=Length(tmp);
end;
//---------------------------------------------------------------------------
//傳入傳出都以 UTF8 字串為準
function _Copy(str: String; idx,len: integer):String; //或稱 AnsiCopy()
var tmp,tmp2:String;
begin
 tmp:=UTF8ToAnsi(str);
 tmp2:=Copy(tmp,idx,len);
 result:=AnsiToUTF8(tmp2);
end;
//---------------------------------------------------------------------------
//傳入傳出都以 UTF8 字串為準
function _Pos(substr,str: String):Integer; //或稱 AnsiPos()
var tmp,tmp2:String;
begin
 tmp:=UTF8ToAnsi(substr);
 tmp2:=UTF8ToAnsi(str);
 result:=Pos(tmp,tmp2);
end;                 
 
常用的 String 處理相關函式有 Length(), Copy(), Pos()  等, 都是以 Ansi String 角度來看結果的, 但在 Lazarus 中 utf8 跟 ansi 混用的情況下, 只好  "以UTF8 String 為參數, 而以 Ansi String 為角度(一個英文是 1 Byte,一個中文字是 2 Byte) " 來編寫新的函式
例如
var tmp: String ; 
    tmp2: String ;
begin
  tmp:='字串處理測試函式';  //default 都是 UTF8 編碼的 String
  tmp2:=_Copy(tmp,5,4);  //以UTF8 String 為參數, 而以 Ansi String 為角度, 取出 tmp 中 第 5 byte 起連續 4 個 byte 的字串 , 取回值也為 UTF8 編碼 String
  ShowMessage(tmp2);  //傳入 ShowMessage() 的 String 需為 UTF8, 故 tmp2 可直接傳入
end; 
 
若要以 Lazarus 內定的 Copy() 取出正確位置的字串, 需要轉來轉去的, 很麻煩
var tmp: String ;
    tmp2: String ;
begin
  tmp:='字串處理測試函式';  //default 都是 UTF8 編碼的 String
  tmp:=Utf8ToAnsi(tmp);  //先把 UTF8 編碼的 String 轉成 Ansi 編碼
  tmp2:=Copy(tmp,5,4);  //用 Lazarus 內建的 Copy() 處理 Ansi String, 取回值也為 Ansi String
  ShowMessage(AnsiToUtf8(tmp2));  //傳入 ShowMessage() 的 String 需為 UTF8, 故需再把 Ansi 再轉回 UTF8
end;
 
補充說明 : 我個人認為 Lazarus 既然要全面使用 UTF8 String , 就不應該再提供像是 Copy() 等這些需傳入 Ansi String 的函式, 而是應提供像我寫的 _Copy() 這種函式 , 傳入傳出都是 UTF8 String , 但內部是以 Ansi String 的角度 (一個英文是 1 Byte,一個中文字是 2 Byte) 來處理字串

  评论这张
 
阅读(1046)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017