在编写TWideMemo数据时,Axolot的XLSReadWriteII 5组件会破坏xls文件的字符串

时间:2016-03-10 12:54:47

标签: sql-server excel delphi unicode

我在windows7上使用Delphi XE10,我已经构建了一个应用程序,我尝试使用XLSReadWriteII 5组件创建一个xls文件。 具体来说,我正在读取先前存储在sql表中并将其导出到xls文件的数据。

现在,虽然一切都可以正常使用ASCII数据,但当我尝试将Unicode数据写入相关工作表时,每当我解析ntext sql字段(包含Unicode字符串)时,我都会遇到问题。正如我们所知,ntext sql的字段由Delphi-XE10作为TWideMemo字段处理,这是处理Unicode数据的相应数据类型字段。

然而,XLSReadWriteII 5组件(即使是为Unicode数据构建),不仅不会在xls文件中写入TWideMemo字段的值,而且最糟糕的是,它会破坏以前写过的文件的任何单元格中的任何字符串数据只接触任何数字类型的数据。而最奇怪的是它在写入过程中不会产生任何错误,我将能够捕获并解决它。 相反,它执行适当的过程,就像一切都很好(如果它只是要写ASCII数据),然后当你尝试打开xls文件(使用excel或open-office)时,它给出了你有几条警告信息,之后你只能看到带有数字类型数据的单元格而且根本没有字符串!!!

仅在解析ntext字段时,我才面临XLS组件的这种奇怪的行为。解析nvarchar字段时根本没问题!!

在这里,我给出了我在我的应用程序中使用的代码:

Procedure WriteDataToCell(adoQ:Tadoquery;row,col,fcol:integer);
var 
   inh:string; data_type:TfieldType;  XLappSun:TXLSReadWriteII5;  sheetSun:Txlsworksheet;
begin
   sheetsun:=xlappsun.SheetByName('sheet1'); 
   data_type:=AdoQ.Fields[fcol].DataType;
   case data_type  of
     ftdate,fttime,ftdatetime,ftTimeStamp: 
           begin
             inh:= formatdatetime('dd/mm/yyyy',AdoQ.Fields[fcol].value);
             sheetsun.asdatetime[col,row-1]:=AdoQ.Fields[fcol].value+0;
           end;
     ftboolean:  
           begin
                 case AdoQ.Fields[fcol].asboolean of
                      true : sheetsun.AsInteger[col,row-1]:=1;
                      false: sheetsun.AsInteger[col,row-1]:=0;
                 end;
           end;
     ftSmallint,ftInteger,ftWord,ftAutoInc,ftLargeint: 
           begin
                sheetsun.asinteger[col,row-1]:=AdoQ.Fields[fcol].asinteger;
           end;
     ftmemo,ftFmtMemo,ftwideMemo:
           begin
               try
                  sheetsun.Asstring[col,row-1]:=AdoQ.Fields[fcol].AsString ;
               except
                  sheetsun.Asstring[col,row-1]:='*****';
               end;
           end;
     ftblob,ftgraphic :
        begin
           sheetsun.Asstring[col,row-1]:='';
        end;
     ftFloat,ftCurrency,ftBCD:
        begin
           sheetsun.AsFloat[col,row-1]:=AdoQ.Fields[fcol].Value;
        end;
     else
          try
             sheetsun.Asstring[col,row-1]:=AdoQ.Fields[fcol].AsString;
          except
             sheetsun.Asstring[col,row-1]:='*****';
          end;
   end;
end; 

我还要说我存储在我的sql表的ntext字段中的数据是通过相关表单中的RTF编辑框提供的,这样才能为我想要的UNICODE文本提供特定的格式存储(如果我想给出像Bold,Underline等特定格式......)。 也许这就是问题的原因......我不知道......

这是DML代码

CREATE TABLE [dbo].[UnicodeT] (
    [countryid] [int] NOT NULL ,
    [Country] [nvarchar] (50) COLLATE Greek_CI_AS NULL ,
    [Comments] [ntext] COLLATE Greek_CI_AS NULL 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

以下是我的数据:

  1. Αυτότοαυτοκίνητοκινείτεπολύγρήγορακαιεπικίνδυνασεαυτόντονστενόδρόμο - Greek
  2. 这条车在这条狭窄的街道上非常快速和危险 - 英语
  3. 这车车是在这个狭窄的街道非常快速和危险 - Chinese_simplified
  4. Tentovůzjevelmirychláanebezpečnávtétoúzkéuličce - Chez
  5. ესესანქანაარისსალიანნაფიდასაშიშიამმიწროწროაზე - Georgian
  6. המכוניתהזאתהיאמאודמהירהומסוכנתהרחובהצרהזה - Hebrew
  7. Þessiebíllermjögfljóturoghættuleguríþessumþröngagötu - Icelandic
  8. この车はこの狭い通りに非常に高速かつ危険です - 日语
  9. ಈಕಾರುಈಕಿರಿದಾದರಸ್ತೆಯಲ್ಲಿವೇಗವಾಗಿಮತ್ತುಅಪಾಯಕಾರಿ - Kannada
  10. اینخودروبسیارسریعوخطرناکدراینخیابانباریکاست - Persian
  11. 以上数据与10种不同语言的短语相同

    我很感激任何可能的帮助......

    提前谢谢

2 个答案:

答案 0 :(得分:1)

问题不在于XLSReadWriteII5。问题在于您的数据库驱动程序或生成XLSX文件的代码。以下是使用Delphi 10 Seattle v23.0.21418.4207和XLSReadWriteII5 v5.20.70进行测试的测试:

  • 创建新的VCL表单应用程序
  • 在表单中添加一个 TButton
  • 将以下代码添加到表单中,将TestFile的名称更改为您自己系统上的合适位置,并将TButton.OnClick事件与TForm1.Button1Click方法相关联。

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form3: TForm3;
    
    implementation
    
    {$R *.dfm}
    
    uses
      XLSReadWriteII5, XLSCmdFormat5, XLSSheetData5, xc12DataStyleSheet5, Xc12Utils5;
    
    const
      TestFile = 'E:\TempFiles\UnicodeTest.xlsx';
    
      aStrings: array[0..5] of String =
        ('Αυτό το αυτοκίνητο κινείτε πολύ γρήγορα και επικίνδυνα σε αυτόν τον στενό δρόμο',
         'This car is very fast and dangerous in this narrow street ',
         '这辆车是在这个狭窄的街道非常快速和危险',
         'Tento vůz je velmi rychlá a nebezpečná v této úzké uličce',
         'ეს მანქანა არის ძალიან სწრაფი და საშიში ამ ვიწრო ქუჩაზე',
         'המכונית הזאת היא מאוד מהירה ומסוכנת הרחוב הצר הזה');
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      XLS: TXLSReadWriteII5;
      i: Integer;
    begin
      XLS := TXLSReadWriteII5.Create(nil);
      XLS.LoadFromFile(TestFile);
      for i := Low(aStrings) to High(aStrings) do
        XLS[0].AsString[5, i] := aStrings[i];
    
      XLS.Write;
      XLS.Free;
      ShowMessage('Done');
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      XLS: TXLSReadWriteII5;
    begin
      XLS := TXLSReadWriteII5.Create(nil);
      XLS[0].AsString[0, 0] := 'This is test ANSI text';
      XLS[0].AsString[0, 1] := 'This is more ANSI text';
      XLS[0].AsInteger[0, 2] := 123;
      XLS[0].AsFloat[0, 3] := 345.67;
      XLS.SaveToFile(TestFile);
    end;
    
    end.
    
  • 运行应用程序,该应用程序将使用FormCreate中的ANSI文本和数字内容创建XLSX文件。

  • 不点击按钮,在Excel中打开文件并确认内容与下图相符,然后退出Excel:

Excel sheet with ANSI text, integer, and float values

  • 点击按钮,将您的示例数据中的前6项(编号为1..6的项目)添加到电子表格的F列,与我们编写的现有数据相同的行之前。

  • 再次在Excel中打开工作表,现在包含下图中的内容。

Same Excel sheet, now containing additional Unicode strings

请注意,第二个图像包含正确的Unicode内容(包括RTL希伯来文本),并且Excel没有显示错误消息。原始的ANSI字符串,整数和浮点值也会正确显示,并且所有内容都在其正确的位置。

以上表明问题不在于XLSReadWriteII5如何编写Unicode,而是在数据库字符集的配置,您正在使用的DBMS组件或Delphi代码本身。但是,它不是XLSReadWriteII5和编写Unicode字符串的问题。

更新:OP一直坚持认为XLSReadWriteII5在从ADO表备注字段读取时无法正确写入字符串。这是按钮点击事件的更新版本,证明错误:

DML(Advantage Database Server v10.10,因为它的方便之处):

Create Table UCodeTest (ID numeric(2, 0), Content NMEMO);
insert into UCodeTest (ID, Content) 
  Values (1, 'Αυτό το αυτοκίνητο κινείτε πολύ γρήγορα και επικίνδυνα σε αυτόν τον στενό δρόμο');
insert into UCodeTest (ID, Content)
  Values (2, 'This car is very fast and dangerous in this narrow street ');
insert into UCodeTest (ID, Content)
  Values (3, '这辆车是在这个狭窄的街道非常快速和危险');
insert into UCodeTest (ID, Content)
  Values (4, 'Tento vůz je velmi rychlá a nebezpečná v této úzké uličce');
insert into UCodeTest (ID, Content)
  Values (5, 'ეს მანქანა არის ძალიან სწრაფი და საშიში ამ ვიწრო ქუჩაზე');
insert into UCodeTest (ID, Content)
  Values (6, 'המכונית הזאת היא מאוד מהירה ומסוכנת הרחוב הצר הזה');

按钮点击事件:

procedure TForm3.Button1Click(Sender: TObject);
var
  XLS: TXLSReadWriteII5;
  i: Integer;
begin
  XLS := TXLSReadWriteII5.Create(nil);
  XLS.LoadFromFile(TestFile);
  ADOQuery1.Open;
  i := 0;
  while not ADOQuery1.Eof do
  begin
    XLS[0].AsString[5, i] := ADOQuery1.FieldByName('Content').AsString;
    Inc(i);
    ADOQuery1.Next;
  end;
  XLS.Write;
  XLS.Free;
  ADOQuery1.Close;
  ShowMessage('Done');
end;

新的Excel工作簿内容,显示从Unicode备注字段读回的数据工作正常,并再次确认问题不在XLSReadWriteII5中:

enter image description here

答案 1 :(得分:-1)

我找到了解决办法,但这并没有真正解释为什么XLS在TWideMemo中的反应如此,但至少提供了解决方案。

具体来说,我在运行时创建了一个TDBMemo的Delphi组件(或者我可以把它放在我的表单上的设计时间......也许更好......),其中我传递了AdoQ的数据源,然后通过了TDBMemo的XLS的价值。 这是它的工作原理:

Procedure WriteDataToCell(adoQ:Tadoquery;row,col,fcol:integer);
var 
   inh:string; data_type:TfieldType;  XLappSun:TXLSReadWriteII5; sheetSun:Txlsworksheet;  
    MyMemo:TDBMemo;
begin
   sheetsun:=xlappsun.SheetByName('sheet1'); 
   data_type:=AdoQ.Fields[fcol].DataType;
   case data_type  of
      ftdate,fttime,ftdatetime,ftTimeStamp: 
       begin
         inh:= formatdatetime('dd/mm/yyyy',AdoQ.Fields[fcol].value);
         sheetsun.asdatetime[col,row-1]:=AdoQ.Fields[fcol].value+0;
       end;
 ftboolean:  
       begin
             case AdoQ.Fields[fcol].asboolean of
                  true : sheetsun.AsInteger[col,row-1]:=1;
                  false: sheetsun.AsInteger[col,row-1]:=0;
             end;
       end;
 ftSmallint,ftInteger,ftWord,ftAutoInc,ftLargeint: 
       begin
            sheetsun.asinteger[col,row-1]:=AdoQ.Fields[fcol].asinteger;
       end;
 ftmemo,ftFmtMemo,ftwideMemo:
       begin
           MyMemo.Create(nil);
           try
              MyMemo.DataSource:= AdoQ.DataSource; 
              sheetsun.Asstring[col,row-1]:=MyMemo.DataSource.DataSet.Fields[fcol].AsString ; 
          //    sheetsun.Asstring[col,row-1]:=AdoQ.Fields[fcol].AsString ;
           except
              sheetsun.Asstring[col,row-1]:='*****';
           end;
           MyMemo.free;
       end;
 ftblob,ftgraphic :
    begin
       sheetsun.Asstring[col,row-1]:='';
    end;
 ftFloat,ftCurrency,ftBCD:
    begin
       sheetsun.AsFloat[col,row-1]:=AdoQ.Fields[fcol].Value;
    end;
 else
      try
         sheetsun.Asstring[col,row-1]:=AdoQ.Fields[fcol].AsString;
      except
         sheetsun.Asstring[col,row-1]:='*****';
      end;
  end;
end; 

这样我就可以检索任何字符集的ntext数据类型的Unicode数据,并保留任何可能的形式。

正如我在回答的开头所说,这种解决方法并没有真正解释为什么XLS会做出这样的反应,或者是否应该使用XLSRWI​​I组件的其他属性或功能来从TWideMemo字段中检索数据。这是为了找到斧头......