分享一下解决 XLSReadWriteII 解析Excel95格式的文件乱码的问题

虽然Excel95格式的文件现在用的少了,但是有些老系统导出的仍然是这种格式的Excel。最近要读取这样格式的文件,发现读出的中文全部乱码。试过XLSReadWriteII 4.00.62、XLSReadWriteII v5.10.25 Cracked for XE2-XE4、TMS FlexCel 5.5和NativeExcel 3.1.0 Beta with XE2 Support,都存在这个问题。以前一直用的是XLSReadWriteII,所以还得用它了。跟踪了好长时间,才发现问题的根本原因在于Excel95格式的文件内部汉字编码为GBK(代码页为936),而Excel97及以上版本采用的是Unicode编码(UTF16,代码页为1200),XLSReadWriteII 4.00.62读取Excel97及以上版本没问题,但是当读取Excel95格式的Excel文件时会产生乱码。
于是解决起来就简单了。方法见下。

顺便说一下,在网上搜索到的解决方案(包括盒子里的)都是修改AddString为AddUnicode,其实这不是根本原因,修改后也没用的(至少对我来说)。

开发环境:简体中文Windows XP SP3,Delphi XE2。

一、读取单元格内容时乱码。这是因为在XLSReadII4.pas单元中的过程"procedure TXLSReadII.RREC_LABEL;"的第1024行有错。原来的第1024行为:
----------
        for i := 0 to Len - 1 do
          WS[i + 1] := WideChar(Data[i]);
----------
此行有错。此时汉字的GBK码已经读出来了(如“入学”两字的十六进制GBK编码为C8EB D1A7),但是用了WideChar以后,是按Unicode码处理的。修改方法如下:
第一步:在出错的行所在的函数体上方加入以下GBK转Unicode代码:

//GBK码转为Unicode(UTF16)码
function GBK_Code_To_Unicode(const S: AnsiString; CodePage: Cardinal = 936) : WideString;//GBK编码的代码页为936
var
  InputLength, OutputLength: Integer;
begin
  InputLength := Length(S);
  OutputLength := MultiByteToWideChar(CodePage, 0, PAnsiChar(S),InputLength, nil, 0);
  SetLength(Result, OutputLength);
  MultiByteToWideChar(CodePage, 0, PAnsiChar(S), InputLength, PWideChar(Result),OutputLength);
end;

第二步:修改上述有错的过程 TXLSReadII.RREC_LABEL 以下所示:
procedure TXLSReadII.RREC_LABEL;
var
  i: integer;
  P: PWordArray;
  WS: WideString;
  ByteArray: array of byte;//存放要显示的字符(或汉字)的字节数组
begin
  InsertRecord := False;
  with PRecLABEL(PBuf)^ do
  begin
    if FXLS.Version > xvExcel97 then
      FXLS.Sheets[FCurrSheet].IntWriteSSTString(Col, Row, FormatIndex,
        ByteStrToWideString(@Data[0], Len))
    else
    begin
      SetLength(WS, Len);
      if Data[0] = 0 then
      begin
        for i := 1 to Len do
          WS[i] := WideChar(Data[i]);
      end
      else if Data[0] = 1 then
      begin
        P := @Data[1];
        for i := 0 to Len - 1 do
          WS[i + 1] := WideChar(P[i]);
      end
      else
      begin
        if FXLS.Version <= xvExcel50 then  //added by seven,2013.6.22
        begin
          //复制提取出来的字节
          SetLength(ByteArray, Len);
          for i := 0 to Len-1 do
          ByteArray[i]:=Data[i];
          WS:=GBK_Code_To_Unicode(AnsiString(ByteArray));//GBK码转为Unicode(UTF16)码.转换后就显示为正确的字符或汉字了
        end
        else
        begin
          for i := 0 to Len - 1 do
          WS[i + 1] := WideChar(Data[i]); //原控件的此行在读取Excel95格式的文件时有问题
        end;

      end;
      FXLS.Sheets[FCurrSheet].IntWriteSSTString(Col, Row, FormatIndex, WS);
    end;
  end;
end;


二、读取Sheet的名称时(即Sheet[n].Name)时乱码的修改方法(同样需要GBK_Code_To_Unicode方法)
修改XLSReadII4.pas单元中的 procedure TXLSReadII.RREC_BOUNDSHEET 过程为以下代码:

procedure TXLSReadII.RREC_BOUNDSHEET;
var
  i: integer;
  WS: WideString;
  Hidden: byte;
  ByteArray: array of byte;//存放要显示的字符(或汉字)的字节数组
begin
  TRecordStorageGlobals(CurrRecs).UpdateInternal(INTERNAL_BOUNDSHEETS);
  InsertRecord := False;
  if FXLS.Version < xvExcel97 then
    with PRecBOUNDSHEET7(PBuf)^ do
    begin
      SetLength(WS, NameLen);
{
      for i := 1 to NameLen do
        WS[i] := WideChar(Name[i - 1]); //此行有问题,故注释掉不用
}
        //复制提取出来的字节
        SetLength(ByteArray, NameLen);
        for i := 0 to NameLen-1 do
          ByteArray[i]:=Name[i];
        WS:=GBK_Code_To_Unicode(AnsiString(ByteArray));//GBK码转为Unicode(UTF16)码.转换后就显示为正确的字符或汉字了

      Hidden := (Options and $0300) shr 8;
    end
    else
      with PRecBOUNDSHEET8(PBuf)^ do
      begin
        WS := ByteToWideString(@Name, NameLen);
        Hidden := Options and $0003;
      end;
  FBoundsheets.AddObject(WS, TObject(Hidden));
end;

三、读取字体名称时乱码的修改方法
XLSFonts4.pas单元的第310行 “function TXFont.CopyFromBuf(Buf: PByteArray): integer; ”调用的ByteStrToWideString函数有问题。这个ByteStrToWideString 方法影响字体名称、公式、单元格注释、打印设置的页头和页脚、单元格的格式化字符。由于我目前只需要读取单元格内容及工作表名,所以这个函数没有进行修改,有需要的请自己解决。

这样修改后,经过测试,Excel95格式、Excel2003和Excel2007及Excel2010格式的文件都可以正常读取出来。希望对大家有点帮助。

seven_14
2013.6.23
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值