其他语言

本类阅读TOP10

·基于Solaris 开发环境的整体构思
·使用AutoMake轻松生成Makefile
·BCB数据库图像保存技术
·GNU中的Makefile
·射频芯片nRF401天线设计的分析
·iframe 的自适应高度
·BCB之Socket通信
·软件企业如何实施CMM
·入门系列--OpenGL最简单的入门
·WIN95中日志钩子(JournalRecord Hook)的使用

分类导航
VC语言Delphi
VB语言ASP
PerlJava
Script数据库
其他语言游戏开发
文件格式网站制作
软件工程.NET开发
VCL中TStrings的重大BUG-不支持Unicode

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站

好久没有来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忠实用户都不希望看到这样。




相关文章

相关软件