好久没有来csdn了,最近主要忙着项目开发,这么旧没有来,想想不能空手而来,所以带来了一篇文章,也是我在项目开发中遇到的问题,希望大家会喜欢。
Unicode已经成为Windows字符的主要编码方式,我想这也是大势所趋,现在软件越来越倾向国际化,而国际化主要的编码方式就是Unicode,不管是单字节英文字符,还是双字节汉字,在Unicode编码格式下都能正常显示。在Windows2000中大到Word,小到Notepad,都能很好的显示Unicode格式的文本文件。可是在我最近的项目中却发现VCL中的TMemo类不支持它,例如如下16进制显示的Unicode文本文件
66 00 67 00 68 00 69 00
但是在C++ builder6中的TMemo控件中则显示为:
在WindowsXP自带的Notepad中显示为:
除了把第一个字符显示出来,其他的字符都被忽略了,这在设计应用中是绝对不允许的,究其原因,原来是TMemo类的Lines属性不支持Unicode,Lines属性是TStings类,TMemo是通过TStrings的LoadFormFile方法代开文件并显示的,在VCL源代码中,我们可以找到如下函数
procedure TStrings.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TStrings.LoadFromStream(Stream: TStream);
var
Size: Integer;
S: string;
begin
BeginUpdate;
try
Size := Stream.Size - Stream.Position;
SetString(S, nil, Size);
Stream.Read(Pointer(S)^, Size);
SetTextStr(S);
finally
EndUpdate;
end;
end;
最后来到SetTextStr函数,由于这个函数的处理,使得Unicode被忽略了
procedure TStrings.SetTextStr(const Value: string);
var
P, Start: PChar;
S: string;
begin
BeginUpdate;
try
Clear;
P := Pointer(Value);
if P <> nil then
while P^ <> #0 do
begin
Start := P;
while not (P^ in [#0, #10, #13]) do Inc(P);
SetString(S, Start, P - Start);
Add(S);
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
end;
finally
EndUpdate;
end;
end;
以上代码片断我们可以清楚的看出Unicode是如何被忽略了,对于Unicode,它的编码方式是不管单字节还是双字节都是都是以双字节编码,单字节的英文则以0来补高位,但是很可惜,在以上函数中只要遇到0就退出了,所以在上面的例子中我们看到了‘f’这唯一的幸存者。
原因找到了,解决起来就倒是很容易,在判断字符串结束时不要以0来定界,而是实际的字符长度,这样就可以解决了,如下代码:
Clear;
P := Pointer(S);
if P <> nil then
begin
iPos := 0;
while (iPos < Size) do // (P^ <> #0) do
begin
Start := P;
while not (P^ in [#0, #10, #13]) do
begin
Inc(P);
Inc(iPos);
end;
SetString(S1, Start, P - Start);
Add(S1);
if (P^ = #13) then
begin
Inc(P);
Inc(iPos);
end;
if (P^ = #10) then
begin
Inc(P);
Inc(iPos);
end;
if (P^ = #0) then
begin
Inc(P);
Inc(iPos);
end;
end;
end;
finally
EndUpdate;
end;
end;
这样TStrings就能很好的支持Unicode字符集了。
写在最后:
其实在C++ Builder中对Unicode支持的还是很好的,VCL提供了WideString类封装了w_char*,而且还提供很多有用的方法用于Ansi与Unicode的相互转换,可能是TMemo太老了,它还来不及改正,说道太老了,VCL里很多控件都应该升级了,比如TRichEdit还是用的Richedit1.0,VCL的界面外观还不能应用XP的外观。
Borland公司的产品推出速度却是“惊人”,很多都还没有用过Delphi6,Delphi7就快要出来了,Borland公司只是在技术上推出新产品,但在服务上却始终不见前进,在这点上Borland要多学习Microsoft,做一个产品要很踏实,升级太平凡会显得“落后的”,记得以前Borland C++ 就是因为过快的发布,而没有足够的测试,导致出现很多Bug,很多用户转向了Microsoft的Visual C++,要不是Delphi力挽狂澜,Borland早就Game Over了,希望Borland不要重蹈覆辙,相信很多和我一样的Borland忠实用户都不希望看到这样。