Recent

Author Topic: Trying to make a CSV to Excel converter  (Read 14407 times)

MISV

  • Hero Member
  • *****
  • Posts: 815
Trying to make a CSV to Excel converter
« on: October 18, 2016, 02:52:12 am »
I am trying to write some code to convert string arrays (CSV) to Excel that will work on Delphi/Windows (some day Lazarus) and Lazarus/Mac.

I am testing in Libre Office / Open Office and Zoho sheets online XLS reader.

I have had my generated XLS documents open in Open Office, but failing in all other without error messages.

I tried create this based on:
- http://blog.runbits.com/post/Native-Export-XLS-without-excel-Delphi.aspx
- http://www.swissdelphicenter.ch/en/showcode.php?id=379
- http://sc.openoffice.org/excelfileformat.pdf

Anoher place the maintainer of  fpspreadsheet guided me in the right direction explaining the code I had (and what I based it on) was full of errors and omissions which could explain he problems.

I think I have now gotten much closer even if it does not open well in Open Office. (And still not e.g. Zoho. Excel97 gives error "can not read file")

Any help will be appreciated. Right now I have begun looking at if it is necessary to define a row structure before simple labels. (I think so even if none of the online examples I found did so) I am also wondering if Dimension should be repeated.




interface

Code: Pascal  [Select][+][-]
  1. //------------------------------------------------------------------------------
  2. (*
  3.   https://www.openoffice.org/sc/excelfileformat.pdf
  4.   - 4.1.1 Worksheet Stream (BIFF2-BIFF4)
  5.  
  6.     BIFF2-BIFF4 = worksheet
  7.     BIFF5 = workbookglobal > multiple worksheets
  8.  
  9.   - 4.2.1 Record Order in a BIFF2 Worksheet Stream
  10. *)
  11. //------------------------------------------------------------------------------
  12. const
  13.   BIFF_EOF = $000A;
  14.   XLS_SHEET = $0010;
  15.   //--
  16.   BOF_BIFF2 = $0009;
  17.   BIFF2_SHEETVIEWSETTINGS_WINDOW2 = $003E;
  18.   LABEL_BIFF2 = $0004;
  19.   DIMENSIONS_BIFF2 = $0000;
  20.   //--
  21.   BOF_BIFF5 = $0809;
  22.   LABEL_BIFF5 = $0204;
  23.   DIMENSIONS_BIFF5 = $0200;
  24.  
  25. type
  26.   TmyDynStringArray = array of string;
  27.   TmyDynTypeStringStringArray = array of TmyDynTypeStringArray;
  28.  
  29. type
  30.   TXLSWriter = class(Tobject)
  31.   private
  32.     procedure WriteByte(B: byte);
  33.     procedure WriteWord(W: word);
  34.   protected
  35.     procedure WriteBOFSheet;
  36.     procedure WriteEOF;
  37.     procedure WriteDimension;
  38.     procedure WriteCodePage;
  39.   public
  40.     Version: Byte;
  41.     DataStream: TMemoryStream;
  42.     MaxCols: Word;
  43.     MaxRows: Word;
  44.     procedure CellStr(ARow, ACol: Word; const AValue: String);
  45.     constructor Create;
  46.     destructor Destroy; override;
  47.   end;
  48.  
  49. function BuildXLSFromStrArrs(const AStrArrs: TmyDynTypeStringStringArray): TMemoryStream;
  50.  


implementation

Code: Pascal  [Select][+][-]
  1. function BuildXLSFromStrArrs(const AStrArrs: TmyDynTypeStringStringArray): TMemoryStream;
  2. var
  3.   MR: Integer;
  4.   MC: Integer;
  5.   vxR: Integer;
  6.   vxC: Integer;
  7.   vxL: Word;
  8.   vxS: String;
  9.   vXLS: TXLSWriter;
  10. begin
  11.   MR := Length(AStrArrs);
  12.   if MR > 0 then
  13.     MC := Length(AStrArrs[0])
  14.   else
  15.     MC := 0;
  16.   ;
  17.   //--
  18.   //
  19.   //--
  20.   vXLS := TXLSWriter.Create;
  21.   vXLS.MaxRows := MR;
  22.   vXLS.MaxCols := MC;
  23.   //--
  24.   //
  25.   //--
  26.   vXLS.WriteBOFSheet;
  27.   vXLS.WriteCodePage;
  28.   vXLS.WriteDimension;
  29.   //--
  30.   // TODO: Looking a specificaion, I may need to implement a row structure here
  31.   //--
  32.   for vxC := 0 to MC-1 do
  33.   begin
  34.     for vxR := 0 to MR-1 do
  35.     begin
  36.       vxS := AStrArrs[vxR,vxC];
  37.       vXLS.CellStr(vxR,vxC,vxS)
  38.     end;
  39.   end;
  40.   //--
  41.   //
  42.   //--
  43.   vXLS.WriteWord(BIFF2_SHEETVIEWSETTINGS_WINDOW2);
  44.   vXLS.WriteByte(0);
  45.   vXLS.WriteByte(0);
  46.   vXLS.WriteByte(0);
  47.   vXLS.WriteByte(0);
  48.   vXLS.WriteByte(0);
  49.   vXLS.WriteWord(0);
  50.   vXLS.WriteWord(0);
  51.   vXLS.WriteByte(1);
  52.   vXLS.WriteByte($FF);
  53.   vXLS.WriteByte($FF);
  54.   vXLS.WriteByte($FF);
  55.   vXLS.WriteByte(0);
  56.   //--
  57.   //
  58.   //--
  59.   vXLS.WriteEOF;
  60.   //--
  61.   //
  62.   //--
  63.   vXLS.DataStream.Position := 0;
  64.   Result := TMemoryStream.Create;
  65.   Result.CopyFrom(vXLS.DataStream, vXLS.DataStream.Size);
  66.   Result.Position := 0;
  67.   vXLS.Free;
  68. end;
  69.  
  70. constructor TXLSWriter.Create;
  71. begin
  72.   inherited Create;
  73.   DataStream := TMemoryStream.Create;
  74.   DataStream.Size := 0;
  75.   MaxCols := 100;
  76.   MaxRows := 65535;
  77. end;
  78.  
  79. destructor TXLSWriter.destroy;
  80. begin
  81.   if DataStream <> nil then
  82.     DataStream.Free
  83.   ;
  84.   inherited;
  85. end;
  86.  
  87. procedure TXLSWriter.WriteBOFSheet;
  88. begin
  89. (*
  90.   https://www.openoffice.org/sc/excelfileformat.pdf#page=135&zoom=auto,104.9,771.1
  91.   - 5.8 BOF – Beginning of File
  92. *)
  93.   WriteWord(BOF_BIFF2);
  94.   WriteWord(XLS_SHEET);
  95. end;
  96.  
  97. procedure TXLSWriter.WriteDimension;
  98. begin
  99. (*
  100.   https://www.openoffice.org/sc/excelfileformat.pdf#page=160&zoom=auto,85.6,581.5
  101.   - 5.35 DIMENSIon
  102. *)
  103.   WriteWord(DIMENSIONS_BIFF2);
  104.   WriteWord(0); // min cols
  105.   WriteWord(MaxRows); // max rows
  106.   WriteWord(0); // min rowss
  107.   WriteWord(MaxCols); // max cols
  108. end;
  109.  
  110. procedure TXLSWriter.CellStr(ARow, ACol: Word; const AValue: String);
  111. var
  112.   TmpBytesLen: Cardinal;
  113.   TmpSizeOfFieldLen: Byte;
  114.   TmpStrExcel: AnsiString;
  115. begin
  116. (*
  117.   See:
  118.   - https://www.openoffice.org/sc/excelfileformat.pdf
  119.  
  120.     "2.5.2 Byte Strings (BIFF2-BIFF5)" states all srings are byte strings
  121.  
  122.    Strings to not have to include #0 terminator per protocol
  123. *)
  124.   WriteWord(LABEL_BIFF2);
  125.   //--
  126.   TmpStrExcel := AValue;
  127.   TmpBytesLen := Length(TmpStrExcel);
  128.   //--
  129.   TmpSizeOfFieldLen := 1;
  130.   if (TmpBytesLen > 255) then
  131.     begin
  132.       TmpBytesLen := 255;
  133.     end
  134.   ;
  135.   //--
  136.   WriteWord(
  137.     2
  138.   + 2
  139.   + 3
  140.   + TmpSizeOfFieldLen
  141.   + TmpBytesLen
  142.   );
  143.   //--
  144.   WriteWord(ARow);
  145.   WriteWord(ACol);
  146.   //--
  147.   WriteByte(0);
  148.   WriteByte(0);
  149.   WriteByte(0);
  150.   //--
  151.   WriteByte(Byte(TmpBytesLen));
  152.   //--
  153.   DataStream.Write(PByte(TmpStrExcel)^, TmpBytesLen);
  154. end;
  155.  
  156. procedure TXLSWriter.WriteByte(B: Byte);
  157. begin
  158.   DataStream.Write(B, 1);
  159. end;
  160.  
  161. procedure TXLSWriter.WriteWord(W: word);
  162. begin
  163.   DataStream.Write(W, 2);
  164. end;
  165.  
  166. procedure TXLSWriter.WriteEOF;
  167. begin
  168. (*
  169.   - https://www.openoffice.org/sc/excelfileformat.pdf#page=161&zoom=auto,113.9,540.2
  170.   5.37 - EOF - End of File
  171. *)
  172.   WriteWord(BIFF_EOF);
  173. end;
  174.  
  175. procedure TXLSWriter.WriteCodePage;
  176. var
  177.   W: Word;
  178. begin
  179. (*
  180.   https://www.openoffice.org/sc/excelfileformat.pdf#page=145&zoom=auto,113.9,771.1
  181.   - 5.17 CODEPAGE
  182. *)
  183.   WriteWord($0042); // OPCODE CODEPAGE
  184.   W := 1252;
  185.   WriteWord(W);
  186. end;
  187.  
« Last Edit: October 18, 2016, 03:48:28 am by MISV »

J-G

  • Hero Member
  • *****
  • Posts: 992
Re: Trying to make a CSV to Excel converter
« Reply #1 on: October 18, 2016, 04:29:50 am »
Why would you need to do this when Excel simply opens any normal .CSV file without user intervention?
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #2 on: October 18, 2016, 12:07:35 pm »
Find here a modified compilable version of your code which creates valid BIFF2 files; the demo runs with both Lazarus and Delphi. Before running, please update the path of the destination file at the end of the main procedure in memstream.SaveToFile(...).
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$IFDEF FPC}
  4.   {$mode objfpc}{$H+}
  5. {$ENDIF}
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. const
  11.   BIFF_EOF = $000A;
  12.   XLS_SHEET = $0010;
  13.   //--
  14.   BOF_BIFF2 = $0009;
  15.   BIFF2_SHEETVIEWSETTINGS_WINDOW2 = $003E;
  16.   LABEL_BIFF2 = $0004;
  17.   DIMENSIONS_BIFF2 = $0000;
  18.   //--
  19.   BOF_BIFF5 = $0809;
  20.   LABEL_BIFF5 = $0204;
  21.   DIMENSIONS_BIFF5 = $0200;
  22.  
  23. type
  24.   TmyDynStringArray = array of string;
  25.   TmyDynStringStringArray = array of TmyDynStringArray;
  26.  
  27. type
  28.   TXLSWriter = class(Tobject)
  29.   private
  30.     procedure WriteBIFFRecordHeader(ARecordID: Word; ARecordSize: Word);
  31.     procedure WriteByte(B: byte);
  32.     procedure WriteWord(W: word);
  33.   protected
  34.     function ConvertToCodePage(AText: String): ansistring;
  35.     procedure WriteBOF;
  36.     procedure WriteEOF;
  37.     procedure WriteDimension;
  38.     procedure WriteCodePage;
  39.   public
  40.     Version: Byte;
  41.     DataStream: TMemoryStream;
  42.     MaxCols: Word;
  43.     MaxRows: Word;
  44.     procedure CellStr(ARow, ACol: Word; const AValue: String);
  45.     constructor Create;
  46.     destructor Destroy; override;
  47.   end;
  48.  
  49. constructor TXLSWriter.Create;
  50. begin
  51.   inherited Create;
  52.   DataStream := TMemoryStream.Create;
  53.   DataStream.Size := 0;
  54.   MaxCols := 256;
  55.   MaxRows := 65535;
  56. end;
  57.  
  58. destructor TXLSWriter.Destroy;
  59. begin
  60.   if DataStream <> nil then
  61.     DataStream.Free;
  62.   inherited;
  63. end;
  64.  
  65. procedure TXLSWriter.WriteBIFFRecordHeader(ARecordID, ARecordSize: word);
  66. begin
  67.   WriteWord(ARecordID);
  68.   WriteWord(ARecordSize);
  69. end;
  70.  
  71. procedure TXLSWriter.WriteBOF;
  72. begin
  73.   (*
  74.     https://www.openoffice.org/sc/excelfileformat.pdf#page=135&zoom=auto,104.9,771.1
  75.     - 5.8 BOF – Beginning of File
  76.   *)
  77.   WriteBIFFRecordHeader(BOF_BIFF2, 4);
  78.   WriteWord(BOF_BIFF2);
  79.   WriteWord(XLS_SHEET);
  80. end;
  81.  
  82. procedure TXLSWriter.WriteDimension;
  83. begin
  84.   (*
  85.     https://www.openoffice.org/sc/excelfileformat.pdf#page=160&zoom=auto,85.6,581.5
  86.     - 5.35 DIMENSIon
  87.   *)
  88.   WriteBIFFRecordHeader(DIMENSIONS_BIFF2, 8);
  89.   WriteWord(0); // min cols
  90.   WriteWord(MaxRows); // max rows
  91.   WriteWord(0); // min rowss
  92.   WriteWord(MaxCols); // max cols
  93. end;
  94.  
  95. function TXLSWriter.ConvertString(AText: String): ansistring;
  96. begin
  97.   {$IFDEF FPC}
  98.   Result := UTF8ToAnsi(AText);
  99.   {$ELSE}
  100.   Result := AnsiString(AText);
  101.   {$ENDIF}
  102. end;
  103.  
  104. procedure TXLSWriter.CellStr(ARow, ACol: Word; const AValue: String);
  105. var
  106.   TmpBytesLen: Cardinal;
  107.   TmpSizeOfFieldLen: Byte;
  108.   TmpStrExcel: AnsiString;
  109. begin
  110. (*
  111.   See:
  112.   - https://www.openoffice.org/sc/excelfileformat.pdf
  113.  
  114.     "2.5.2 Byte Strings (BIFF2-BIFF5)" states all srings are byte strings
  115.  
  116.    Strings to not have to include #0 terminator per protocol
  117. *)
  118.   if AValue = '' then
  119.     exit;
  120.  
  121.   TmpStrExcel := ConvertToCodePage(AValue);  // To do for Delphi: Convert unicodestring to ansistring
  122.   TmpBytesLen := Length(TmpStrExcel);
  123.   //--
  124.   TmpSizeOfFieldLen := 1;
  125.   if (TmpBytesLen > 255) then
  126.     begin
  127.       TmpBytesLen := 255;
  128.     end
  129.   ;
  130.   //--
  131.   WriteBIFFRecordHeader(LABEL_BIFF2, 2 + 2 + 3 + TmpSizeOfFieldLen + TmpBytesLen);
  132.   //--
  133.   WriteWord(ARow);
  134.   WriteWord(ACol);
  135.   //--
  136.   WriteByte(0);
  137.   WriteByte(0);
  138.   WriteByte(0);
  139.   //--
  140.   WriteByte(Byte(TmpBytesLen));
  141.   //--
  142.   DataStream.Write(TmpStrExcel[1], TmpBytesLen);
  143. end;
  144.  
  145. procedure TXLSWriter.WriteByte(B: Byte);
  146. begin
  147.   DataStream.Write(B, 1);
  148. end;
  149.  
  150. procedure TXLSWriter.WriteWord(W: word);
  151. begin
  152.   DataStream.Write(W, 2);
  153. end;
  154.  
  155. procedure TXLSWriter.WriteEOF;
  156. begin
  157. (*
  158.   - https://www.openoffice.org/sc/excelfileformat.pdf#page=161&zoom=auto,113.9,540.2
  159.   5.37 - EOF - End of File
  160. *)
  161.   WriteBIFFRecordHeader(BIFF_EOF, 0);
  162. end;
  163.  
  164. procedure TXLSWriter.WriteCodePage;
  165. var
  166.   W: Word;
  167. begin
  168. (*
  169.   https://www.openoffice.org/sc/excelfileformat.pdf#page=145&zoom=auto,113.9,771.1
  170.   - 5.17 CODEPAGE
  171. *)
  172.   WriteBIFFRecordHeader($0042, 2);  // $0042 = OPCODE CODEPAGE
  173.   W := 1252;
  174.   WriteWord(W);
  175. end;
  176.  
  177.  
  178. function BuildXLSFromStrArrs(const AStrArrs: TmyDynStringStringArray): TMemoryStream;
  179. var
  180.   MR: Integer;
  181.   MC: Integer;
  182.   vxR: Integer;
  183.   vxC: Integer;
  184.   vxL: Word;
  185.   vxS: String;
  186.   vXLS: TXLSWriter;
  187. begin
  188.   MR := Length(AStrArrs);
  189.   if MR > 0 then
  190.     MC := Length(AStrArrs[0])
  191.   else
  192.     MC := 0;
  193.   ;
  194.   //--
  195.   //
  196.   //--
  197.   vXLS := TXLSWriter.Create;
  198.   vXLS.MaxRows := MR;
  199.   vXLS.MaxCols := MC;
  200.   //--
  201.   //
  202.   //--
  203.   vXLS.WriteBOF;
  204.   //vXLS.WriteCodePage;   // Not absolutely necessary if you use strings of your code page
  205.   vXLS.WriteDimension;
  206.   //--
  207.   // TODO: Looking a specificaion, I may need to implement a row structure here
  208.   // --- wp: not absolutely necessary, only if you want to change row heights and add row formats
  209.   //--
  210.   for vxC := 0 to MC-1 do
  211.   begin
  212.     for vxR := 0 to MR-1 do
  213.     begin
  214.       vxS := AStrArrs[vxR,vxC];
  215.       vXLS.CellStr(vxR,vxC,vxS)
  216.     end;
  217.   end;
  218.   vXLS.WriteEOF;
  219.  
  220.   vXLS.DataStream.Position := 0;
  221.   Result := TMemoryStream.Create;
  222.   Result.CopyFrom(vXLS.DataStream, vXLS.DataStream.Size);
  223.   Result.Position := 0;
  224.   vXLS.Free;
  225. end;
  226.  
  227. var
  228.   memstream: TMemoryStream;
  229.   AStrArrs: TmyDynStringStringArray;
  230.  
  231. begin
  232.   SetLength(AStrArrs, 3);
  233.   SetLength(AStrArrs[0], 2);
  234.   SetLength(AStrArrs[1], 2);
  235.   SetLength(AStrArrs[2], 2);
  236.   AStrArrs[0, 0] := 'ÄÖÜ';
  237.   AStrArrs[1, 0] := 'B';
  238.   AStrArrs[2, 0] := 'C';
  239.   AStrArrs[0, 1] := 'D';
  240.   AStrArrs[1, 1] := 'E';
  241.   AStrArrs[2, 1] := 'F';
  242.   memstream := BuildXLSFromStrArrs(AStrArrs);
  243.   memstream.SaveToFile('d:\test.xls');
  244.   memstream.Free;
  245.  
  246. end.

The problem in your code was that you did not write valid BIFF records consisting of a header and data. A BIFF record header consists of the recordID and the size of the following data, you only wrote the recordID. An important detail is also that strings in the BIFF2 file are ansistrings, but Lazarus and Delphi don't use these strings by default any more. Therefore you must convert the strings to ansistrings -> function ConvertString():ansistring. An explicit CODEPAGE record in not required if you only write characters of your system codepage (which seems to be 1252).

The next time when you post questions I would appreciate if you could provide a compilable small project - this way it would be easier for me to get started. Pack the pas, lfm, lpr, and lpi files of a Lazarus project into a single zip which you can upload here as an attachment (no binary or other compiler-generated files, please).

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #3 on: October 18, 2016, 01:30:36 pm »
Why would you need to do this when Excel simply opens any normal .CSV file without user intervention?

Microsoft Online Office can apparently not open CSV files

wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #4 on: October 18, 2016, 02:12:16 pm »
I'm not registered there, but I cannot imagine that they cannot open CSV files. There is certainly some confusion about CSV files - essentially, these are plain text files, columns are separated by a comma or some other character (usually, tab or semicolon), and rows are separated by line feeds, individual cells are sometimes quoted, sometimes not. Did you try "Text files"? That's just the same. Off-line Excel opens a wizard which helps to setup the conversion - online Excel must have it as well, I am sure.

For conversion of CSV files to Excel or OpenDocument files you could also consider using fpspreadsheet: http://wiki.lazarus.freepascal.org/FPSpreadsheet

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #5 on: October 18, 2016, 03:00:41 pm »
I'm not registered there, but I cannot imagine that they cannot open CSV files. There is certainly some confusion about CSV files - essentially, these are plain text files, columns are separated by a comma or some other character (usually, tab or semicolon), and rows are separated by line feeds, individual cells are sometimes quoted, sometimes not. Did you try "Text files"? That's just the same. Off-line Excel opens a wizard which helps to setup the conversion - online Excel must have it as well, I am sure.

For conversion of CSV files to Excel or OpenDocument files you could also consider using fpspreadsheet: http://wiki.lazarus.freepascal.org/FPSpreadsheet

I was in disbelieve as well (stating the exact same things as you) until I was sent these URLs:

 http://answers.microsoft.com/en-us/msoffice/forum/msoffice_excel-mso_other/is-it-possible-to-open-a-csv-file-in-excel-in/582c78e4-74b8-4fc6-9053-6e02abc5aceb

I think it is of strategic reasons MS chose not to support, but I have no idea.
« Last Edit: October 18, 2016, 03:07:13 pm by MISV »

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #6 on: October 18, 2016, 03:06:21 pm »
Find here a modified compilable version of your code which creates valid BIFF2 files;

Thank you very much - going over it now.

Quote
The problem in your code was that you did not write valid BIFF records consisting of a header and data. A BIFF record header consists of the recordID and the size of the following data, you only wrote the recordID.

Argh. I originally had sizes, but then figured that for record IDs that had a specified size - the size was probably not supposed to be there (somehow I missed that in specification)

Quote
Therefore you must convert the strings to ansistrings

Roger. Just waned to create simplest possible example.


Quote
The next time when you post questions I would appreciate if you could provide a compilable small project

You are correct. Will do.

wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #7 on: October 18, 2016, 03:14:44 pm »
And the last document that you link says that it will not open BIFF2 (ancient Excel2.1 format) either... You'll have to write Excel97, Excel xlsx, or OpenDocument. These file formats are too complex for a short procedure. Please use fpspreadsheet. But from your title in your original Stackoverflow question (https://stackoverflow.com/questions/40065903/simple-excel-xls-files-created-with-delphi-dont-open-in-excel-reader-tools) is suspect that you want to use this with Delphi; unfortunately fpspreadsheets relies on some typical FPC units (avl_tree) and therefore does not work with Delphi.  (it is not available for Delphi, though). If you really need delphi then you could try zexmlss (http://avemey.com/files.php?lang=en) (I never checked it, though).

wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #8 on: October 18, 2016, 03:22:44 pm »
Argh. I originally had sizes, but then figured that for record IDs that had a specified size - the size was probably not supposed to be there (somehow I missed that in specification)
The size is not specified as a single value because it often depends on the record content. Since the record size to be written is without the header you must add the byte sizes of the data elements (like you did in the CellStr method). The DIMENSION record, for example, contains the indexes for the first and last row and columns, each specified as a WORD. Therefore the size is 4*SizeOf(Word) = 8.

If you really are interested in the internal structure of BIFF files you could have a look at my BIFFExplorer which I used a lot for analyzing and understanding these files. You can find it at https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/applications/biffexplorer/ (it does require some third-party components, though).

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #9 on: October 19, 2016, 12:32:53 am »
Just wanted to confirm your code works in Excel97 and Open Office now. In Zoho it does not.

I started conversion to BIFF5. I have not implemened the starting section yet. I will try do so. Still opens in Excel and Open Office. Still not in Zoho.

I am testing against Zoho and other online XLS readers since I do not myself have an Microsoft Online Office subscription.

Thank you again for your help. Even if I ultimately fail, I am still happy to have working enough BIFF2/BIFF5 that Excel97 can read it.

I will check out your tool :)


wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #10 on: October 19, 2016, 01:22:39 am »
Never heard of Zoho... It could be that the files written by the modified code above do not contain all the records that Zoho expects. Possible candidates of missing records are the FORMATCOUNT, FORMAT, FONT and XF records - see the excelfileformat doc for details. If you have problems feel free to ask again (here, not at stackoverflow :-X )

I don't think that you can write a valid BIFF5 file easily because this has a very complicated file structure of several "streams" and a directory embedded into the same xls file. (We only have BIFF5 and BIFF8 in fpspreadsheet because José Mejuto wrote the units needed). The documentation for this is not contained in the excelfileformat.pdf, but you may seek for compdocfileformat.pdf if you really want to jump into this adventure.


VTwin

  • Hero Member
  • *****
  • Posts: 1227
  • Former Turbo Pascal 3 user
Re: Trying to make a CSV to Excel converter
« Reply #11 on: October 19, 2016, 01:57:49 am »
Have you looked at FPSpreadsheet? It works very well to read and write Excel and LibreOffice files. I use my own code to read and write csv and tab delimited files, but FPSpreadsheet and TStringList are csv friendly.

Cheers,
VTwin

Edit: I see you mentioned fpspreadsheet already. I guess it does not meet your needs?
« Last Edit: October 19, 2016, 02:24:06 am by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

Free Pascal Compiler 3.2.2
macOS 15.3.2: Lazarus 3.8 (64 bit Cocoa M1)
Ubuntu 18.04.3: Lazarus 3.8 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 3.8 (64 bit on VBox)

wp

  • Hero Member
  • *****
  • Posts: 13422
Re: Trying to make a CSV to Excel converter
« Reply #12 on: October 19, 2016, 11:46:47 am »
Just wanted to confirm your code works in Excel97 and Open Office now. In Zoho it does not.
It would be interesting if you could check whether the files in the attached zip can be opened in Zoho. These are files generates by fpspreadsheet (test-b2.xls and test-b5.xls are BIFF2 and BIFF5 versions of Excel, respectively, test.xls is BIFF8, test.xlsx is Excel2007, test.ods is OpenDocument Calc, test.xml is Excel 2003-XML (rarely used))

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #13 on: October 19, 2016, 02:09:01 pm »
Zoho
https://sheet.zoho.com/sheet/excelviewer
(Reason I use this is that I saw it mentioned many places. However my experience wih Zoho is similar to that of other online excel reader)

test.xls works
test.xlsx works
test-b2.xls fails
test-b5.xls works
test.xml not accepted
test.ods works

where "works" = no errors reported
« Last Edit: October 19, 2016, 02:28:46 pm by MISV »

MISV

  • Hero Member
  • *****
  • Posts: 815
Re: Trying to make a CSV to Excel converter
« Reply #14 on: October 19, 2016, 02:31:11 pm »
Never heard of Zoho... It could be that the files written by the modified code above do not contain all the records that Zoho expects. Possible candidates of missing records are the FORMATCOUNT, FORMAT, FONT and XF records - see the excelfileformat doc for details. If you have problems feel free to ask again (here, not at stackoverflow :-X )

I don't think that you can write a valid BIFF5 file easily because this has a very complicated file structure of several "streams" and a directory embedded into the same xls file. (We only have BIFF5 and BIFF8 in fpspreadsheet because José Mejuto wrote the units needed). The documentation for this is not contained in the excelfileformat.pdf, but you may seek for compdocfileformat.pdf if you really want to jump into this adventure.

I was hoping at inserting minimum header data required for one sheet. But you may be right - may not be worth pursuing.
« Last Edit: October 19, 2016, 02:59:58 pm by MISV »

 

TinyPortal © 2005-2018