Lazarus中文社区

 找回密码
 立即注册(注册审核可向QQ群索取)

QQ登录

只需一步,快速开始

Lazarus IDE and 组件 下载地址版权申明
查看: 11343|回复: 4

Lazarus中文乱码问题

[复制链接]

该用户从未签到

发表于 2012-11-30 10:27:24 | 显示全部楼层 |阅读模式
我使用的是V0.9.30。
在定义操作String字符串,有的是 ANSI 编码,有的是UTF8编码,转来转去就转晕了.下面将我遇到的类似问题罗列出来,以供借鉴.欢迎补充.
1.MEMO的SaveToFile和LoadFromFile,文件名需要utf8toansi();
2.ExtractFilePath是ANSI ,直接贴一段代码如下:其中getAppPath取自ExtractFilePath,后面+‘\'

s2:=getAppPath+'backdata';
  if Not DirectoryExists(s2) then CreateDir(s2);
  edit2.text:=ansitoutf8(s2)+'\' ;
   s1:= getapppath+'ini\unitcode.ucd'; //这是乱码
   s1:=ansitoutf8(s1);
   if fileexists(utf8toansi(s1)) then   //和文件路径有关的必须UTF8
   begin
      GridLoadFromfile(stringgrid1,s1);
   end;
                          

评分

参与人数 1威望 +1 收起 理由
bugxiong + 1 相互学习,共同进步!

查看全部评分

回复

使用道具 举报

该用户从未签到

发表于 2012-11-30 11:17:42 | 显示全部楼层
相互学习,共同进步!
回复 支持 反对

使用道具 举报

该用户从未签到

 楼主| 发表于 2012-12-5 08:18:24 | 显示全部楼层
学LA解决中文乱码看来是第一课.直接转来一篇:

LAZARUS的文本处理基础

离线wnhoo

前两天给LAZ的一个文本问题困扰了,同样的代码:Length('Hellö Wöld'),竟然在不同工程里的执行结果不同。所以好好查了下有关资料,现在整理出来,做个总结。有不对的和欠缺的欢迎大家指正。
对UNICODE UTF-8 UTF-16不熟的,先补充下理论:http://blog.sina.com.cn/s/blog_5d8cc6410100e432.html


FREE PASCAL的有关数据类型:
char 1个字节的字符
widechar 2个字节的字符
string 代表char数组。{$H+}是相当于ansistring;{$H-}时或有指明长度时(比如:var lTxt:string[10]),相当于shortstring。
shortstring 代表长度不超过255个字节的char数组。内部结构包括:长度(1字节)、char数组。
ansistring 代表不定长的char数组。内部结构包括:引用计数、char数组、文本结束符(值为0,1字节)。
对应的指针PChar,表示指向一char数组,结尾有文本结束符(值为0,1字节)。
unicodestring 代表不定长的widechar数组。内部结构包括:引用计数、widechar数组、文本结束符(值为0,2字节)。
对应的指针PUnicodeChar,表示指向一widechar数组,结尾有文本结束符(值为0,2字节)。
widestring 代表不定长的widechar数组。跟unicodestring类似,差别在于widestring内部结构里没有引用计数,并且在WINDOWS下使用会用WIN特定的功能分配内存以便支持OLE。

请注意:
1.FPC会透明处理shortstring、ansistring、unicodestring和widestring互相之间的赋值,但不会处理字符串的编码问题。
也就是说这四种类型的变量里存放的是ANSI还是UTF-8还是UTF-16,FPC不关心也不处理。估计只处理数据存放的空间之间的转换。
编码问题需要开发人员使用函数来处理。一般shortstring ansistring存放ANSI和UTF-8,unicodestring widestring存放UTF-16。
2.一般引用计数就是为了编译器自动分配和自动回收数据所占空间,那么widestring按文档所说就是编译器不会自动回收空间了,是否如此有谁知道?
3.字符串编码之间的转换,LAZ提供了一堆函数,若使用0.9.30及以前版本的,放在LCLProc单元里;若使用0.9.31及以后版本的,放在lazutf8单元里。
ANSI与UTF-8之间的转换用SysToUTF8 UTF8ToSys,UTF-8与UTF-16之间用UTF8ToUTF16 UTF16ToUTF8。


以下LAZARUS和FREE PASCAL支持的字符串编码方式: 支持的编码方式
RTL(截止至2.6.0) ANSI
FCL(截止至2.6.0) ANSI
LAZARUS(包括LCL)0.9.25以前版本的 ANSI
LAZARUS(包括LCL)0.9.25及其以后版本的 UTF-8(除了gtk 1)
  
  


LCL库里大多数是可视控件,所以可视控件里的文本类型属性若要显示正确,从0.9.25开始传进去的需要UTF-8编码的字符串。而
FREEPASCAL的核心库RTL FCL一直只支持ANSI,也就是程序运行所在操作系统的本地编码。

说回 Length('Hellö Wöld') 问题,
'Hellö Wöld'这类字符串常量,最初的编码是由.pas文件(或窗体的.lfm文件)的编码方式决定的。在LAZARUS的编辑窗点鼠标右键 File Settings=>Encoding那里可以设置。
若编码方式是UTF-8,UTF-8是变长的编码方式,英文只占1个字节,其它的占1~6个字节,‘ö’占2个字节,而Length函数属于RTL库,所以长度返回12.
若编码方式是ANSI,保存到文件时,‘ö’不能转换出ANSI对应的,所以用了一个莫名的字符代替。结果运行起来长度返回10.
建议在LAZARUS下,用UTF-8编码,网站上也是推荐用这编码。

详细请看:http://wiki.lazarus.freepascal.org/LCL_Unicode_Support


--------------------------------------------------------------------------------

高文章啊。
求教下
若编码方式是ANSI,保存到文件时,‘ö’不能转换出ANSI对应的,所以用了一个莫名的字符代替。结果运行起来长度返回10.
这个应该怎么解决??
--------------------------------------------------------------------------------

更正一点:
在看了有关源代码后,发现对于LAZARUS,ANSI编码应该不是本地操作系统的编码体系,因为从转换过程来看没识别当前系统环境。具体是哪套编码标准暂时不清楚,估计DELPHI的ANSI编码应该是本地编码,否则早有BUG汇报了。

对于DELPHI2007,也有类似机制,在编辑窗口点右键File Format=>...就可以将源代码文件按设置的编码规则保存。但DELPHI的编译器能在编译时把字符串常量统一转换成ANSI编码。
然而对于LAZARUS,FPC在编译时并没有把字符串常量统一转换成UTF-8或者ANSI,而是源代码是什么编码那么字符串就是什么编码。

而且,LAZARUS在源代码的处理上好像有BUG:
1.编辑器不能自动识别源代码的编码规则。
2.如果选UTF-8 WITH BOM,LENGTH函数不能正确处理‘Hellö Wöld’
3.如果选ANSI,在编辑源代码时有时会死掉LAZARUS。


所以,如果想源代码在DELPHI和LAZARUS下通用,编码方式只能选UTF-8。

--------------------------------------------------------------------------------

用UTF8ToAnsiString
回复 支持 反对

使用道具 举报

该用户从未签到

 楼主| 发表于 2012-12-25 23:01:31 | 显示全部楼层
转一篇:http://www.pascalbbs.com/article-56216.html
Lazarus 對於 String 編碼的 BUG 與處理
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));
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) 來處理字串



回复 支持 反对

使用道具 举报

该用户从未签到

 楼主| 发表于 2012-12-25 23:10:29 | 显示全部楼层
官方网站的说明:别看仅是个链接,刚开始就是不知在那.
http://wiki.lazarus.freepascal.o ... 4.E4.BE.8B.E5.AD.90

在版本0.9.31+起,LCLProc中的的这些函数为了兼容老代码依然保留,但是真正的处理代码(LCLProc中的这些函数只剩下声明了)都转移到了lazutils这个package的lazutf8单元中了。
回复 支持 反对

使用道具 举报

*滑块验证:

本版积分规则

QQ|手机版|小黑屋|Lazarus中国|Lazarus中文社区 ( 鄂ICP备16006501号-1 )

GMT+8, 2025-5-2 20:33 , Processed in 0.037008 second(s), 14 queries , Redis On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表