[gelöst] fpspreedsheet - Frage

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
dash_develop
Beiträge: 38
Registriert: So 15. Mai 2016, 13:33

[gelöst] fpspreedsheet - Frage

Beitrag von dash_develop »

Hallo zusammen,

habe mal wieder eine Frage zu meinem Code:
Ich würde gerne ein WS auswählen und das gewählte WS als CSV speichern

Code: Alles auswählen

 
procedure TForm1.Button2Click(Sender: TObject);
var
  wb : TsWorkbook;
  ws : TsWorksheet;
begin
  wb := TsWorkbook.Create;
  ws := TsWorksheet.Create;
  try
  wb.ReadFromFile(pfad + 'Excel.xlsm');
  ws := wb.GetLastWorksheet();
    ShowMessage(PChar(ws));
  wb.WriteToFile(pfad + 'Excel.csv');
  finally
  end;
end;
 


Wieso speichert er mir trotzdem das erste Worksheet als CSV und nicht das letzte Worksheet?
Ich bekomme auch nur ein Fragezeichen als Showmessage.. Obwohl da "Test" stehen müsste.

Gruß
dash_develop
Zuletzt geändert von dash_develop am Mo 3. Apr 2017, 19:00, insgesamt 1-mal geändert.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: fpspreedsheet - Frage

Beitrag von wp_xyz »

dash_develop hat geschrieben:

Code: Alles auswählen

  ....
  ws := TsWorksheet.Create;
  ....


Worksheets darfst du nicht "createn". Sie werden vom Workbook automatisch erzeugt, entsprechend den Worksheets in der Datei. Manuell kannst du einem Workbook ein leeres Sheet per Workbook.AddWorksheet('eindeutiger Name') hinzufügen.

In deinem Code fehlt außerdem das wb.Free, mit dem das Workbook wieder freigegeben wird. Das Workbook kümmert sich auch um die Worksheets, so dass ein Worksheet.Free nicht nötig (ja sogar falsch) ist.

dash_develop hat geschrieben:Ich bekomme auch nur ein Fragezeichen als Showmessage.. Obwohl da "Test" stehen müsste.

"ShowMessage (PChar(worksheet))" schlägt fehlt, da das Worksheet kein String ist, sondern eine Instanz der Klasse TsWorksheet. Durch den Cast auf PChar wird der Beginn des vom Workbook belegten Speicherbereichs als String interpretiert - da das aber im allgemeinen keine gültigen UTF8-Zeichen sind, siehst du das Fragezeichen. Aber möglicherweise meinst du den Namen des Worksheets:

Code: Alles auswählen

  ShowMessage(ws.Name); 

(Wird aber in dem obigen Code nicht wie gewünscht funktionieren, da das Worksheet auf "illegale" Weise erzeugt worden ist).

dash_develop hat geschrieben:Wieso speichert er mir trotzdem das erste Worksheet als CSV und nicht das letzte Worksheet?

Beim Zugriff auf CSV-Dateien gibt es einige Parameter, die vor dem Zugriff festgelegt werden müssen, wenn man nicht die Standardwerte haben will. Die Parameter sind in dem globalen Record CSVParams zusammengefasst:

Code: Alles auswählen

type
  TsCSVParams = record   // W = writing, R = reading, RW = reading/writing
    SheetIndex: Integer;             // W: Index of the sheet to be written
    LineEnding: TsCSVLineEnding;     // W: Specification for line ending to be written
    Delimiter: Char;                 // RW: Column delimiter
    QuoteChar: Char;                 // RW: Character for quoting texts
    Encoding: String;                // RW: Encoding of file (code page, such as "utf8", "cp1252" etc)
    DetectContentType: Boolean;      // R: try to convert strings to content types
    NumberFormat: String;            // W: if empty write numbers like in sheet, otherwise use this format
    AutoDetectNumberFormat: Boolean; // R: automatically detects decimal/thousand separator used in numbers
    TrueText: String;                // RW: String for boolean TRUE
    FalseText: String;               // RW: String for boolean FALSE
    FormatSettings: TFormatSettings; // RW: add'l parameters for conversion
  end;
 
var
  CSVParams: TsCSVParams = (
    SheetIndex: 0;
    LineEnding: leSystem;
    Delimiter: ';';
    QuoteChar: '"';
    Encoding: '';    // '' = auto-detect when reading, UTF8 when writing
    DetectContentType: true;
    NumberFormat: '';
    AutoDetectNumberFormat: true;
    TrueText: 'TRUE';
    FalseText: 'FALSE';
  {%H-})


CSVParams.SheetIndex ist der Index des Worksheets, das geschrieben werden soll. Um das letzte Worksheet zu schreiben, machst du dann dieses:

Code: Alles auswählen

procedure TForm1.Button2Click(Sender: TObject);
var
  wb : TsWorkbook;
begin
  wb := TsWorkbook.Create;
  try
    wb.ReadFromFile(pfad + 'Excel.xlsm');
    CSVParams.SheetIndex := wb.GetWorksheetCount - 1;   // letztes Worksheet schreiben
    CSVParams.FormatSettings.DecimalSeparator := '.';   // Zahlen mit Dezimalpunkt schreiben -- nur als Beispiel. Oder:
    CSVParams.Delimiter := #9;                          // Tabulator als Trennzeichen
    wb.WriteToFile(pfad + 'Excel.csv');
  finally
    wb.Free;
  end;
end;

dash_develop
Beiträge: 38
Registriert: So 15. Mai 2016, 13:33

Re: fpspreedsheet - Frage

Beitrag von dash_develop »

Hallo wp_xyz,

erstmal danke mal wieder für deine Mühe. Jetzt habe ich das mit dem Worksheet verstanden, aber was haben CSVParams mit den FPSpreadsheet zu tuen?
Darf ich dich mal Fragen, woher du das alles so weißt? :D :P

Nagut, ich habe das Ganze mal probiert. Jedoch schreibt der mir kurioseweise immer noch das erste Worksheet in die CSV, obwohl ich deinem Code gefolgt bin.

Code: Alles auswählen

CSVParams.SheetIndex := wb.GetWorksheetCount - 1;   // letztes Worksheet schreiben

Müsste das eigentlich nicht + 1 sein, aufgrund das er bei 0 anfängt und hochzählt?

Habe mal frecher Weise dein Code 1 zu 1 übernommen, jedoch ohne Erfolg:

Code: Alles auswählen

 
unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  fpstypes, fpspreadsheet, xlsxooxml, fpscsv;
 
type
 
  { TForm1 }
 
    TForm1 = class(TForm)
    Button: TButton;
    procedure ButtonClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
var
  Form1: TForm1;
  CSVParams: TsCSVParams;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.ButtonClick(Sender: TObject);
var
  wb : TsWorkbook;
begin
  wb := TsWorkbook.Create;
     try
        wb.ReadFromFile('C:\TEMP\Test.xlsm');
        CSVParams.SheetIndex := wb.GetWorksheetCount - 1;   // letztes Worksheet schreiben
        wb.WriteToFile('C:\TEMP\Test.csv');
     finally
        wb.Free;
     end;
end;
 
end
 


Ist es möglich das er gar nicht von dem CSVParams gebrauch macht?
Habe gerade probiert den Delimiter zu ändern, genauso kein Erfolg...

dash_develop
Beiträge: 38
Registriert: So 15. Mai 2016, 13:33

Re: fpspreedsheet - Frage

Beitrag von dash_develop »

Müsste ich mit diesem Code nicht alle Worksheets als CSV bekommen?

Code: Alles auswählen

procedure TForm1.ButtonClick(Sender: TObject);
var
  wb : TsWorkbook;
  i : Integer;
begin
  wb := TsWorkbook.Create;
     try
        wb.ReadFromFile('C:\TEMP\ExcelFiles\Test.xlsm');
 
        for i = 0  to wb.GetWorksheetCount-1 do begin
         wb.WriteToFile('C:\TEMP\CSVFiles\'+FormatDateTime('yyyymmdd_hhnnss', now)+'_Test.csv');
        end;
     finally
        wb.Free;
     end;
end;


Über jegliche Hilfe wäre ich sehr dankbar.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: fpspreedsheet - Frage

Beitrag von wp_xyz »

Hier ist ein Konsolen-Beispiel, in dem ein Spreadsheet mit zwei Seiten erzeugt wird: Jede Seite enthält in Zelle A1 einen Hinweis auf den Namen der Seite (Tabelle 1 oder 2). Wenn du weiter unten in den CSVParams den SheetIndex auf 1 einstellt, enthält die Datei die Seite mit dem Hinweis auf "Tabelle 2" -- CSVParams funktionieren also.

Code: Alles auswählen

program mycsvwrite;
 
{$mode objfpc}{$H+}
 
uses
  Classes, SysUtils, fpstypes, fpspreadsheet, fpscsv;
 
var
  MyWorkbook: TsWorkbook;
  MyWorksheet: TsWorksheet;
  MyDir: string;
 
begin
  MyDir := ExtractFilePath(ParamStr(0));
 
  MyWorkbook := TsWorkbook.Create;
  try
    // Tabelle 1
    MyWorksheet := MyWorkbook.AddWorksheet('Tabelle 1');
    MyWorksheet.WriteText(0, 0, 'Dieser Text steht auf "Tabelle 1"');
 
    // Tabelle 2
    MyWorksheet := MyWorkbook.AddWorksheet('Tabelle 2');
    MyWorksheet.WriteNumber(0, 1, 1.0);
    MyWorksheet.WriteText(0, 0, 'Und dieser Text steht auf "Tabelle 2"');
 
    CSVParams.SheetIndex := 1;
    CSVParams.Delimiter := #9;
    CSVParams.FormatSettings.DecimalSeparator := '.';
    CSVParams.NumberFormat := '%.2f'// Schreibe Zahlen mit 2 Dezimalstellen
    CSVParams.QuoteChar := '''';
 
    MyWorkbook.WriteToFile(MyDir + 'test.csv', sfCSV, true);
  finally
    MyWorkbook.Free;
  end;
end.


Möglicherweise enthält deine Test-Datei nur 1 Seite? Die test.xlsm, die du früher einmal hochgeladen hast, jedenfalls enthält nur 1 Sheet. (Übrigens, warum ist die Datei plötzlich nicht mehr im Forum? Es könnte ja sein, dass jemand mal auf diesen Thread stößt und die Datei sehen möchte...).

Der Zusammenhang zwischen Workbook/Worksheet und CSVParams ist nicht offensichtlich. Je nach Dateiformat, erzeugt das Workbook eine spezielle Writer-Klasse, um sich im entsprechenden Format in die Datei zu schreiben. Welches Format genommen wird, kann man (u.a.) durch den Formatparameter, hier sfCSV, angeben. Der spezielle Writer für CSV holt sich zusätzliche Parameter aus dem Record CSVParams.

Wegen des +1: Nein, -1 ist schon richtig. Wenn das Workbook z.B. 2 Seiten enthält, haben diese die Nummern 0 und 1, eben weil die Nummerierung mit 0 beginnt - die Nummer der letzen Seite ist also um 1 niedriger als die Anzahl der Seiten.

Woher ich das alles weiß? Weil ich es selbst geschrieben habe. Ansonsten steht das auch im wiki: http://wiki.lazarus.freepascal.org/FPSp ... _CSV_files

Dein Code zum Speichern aller Seiten ist prinzipiell richtig, allerdings sind die Dateinamen nicht gut: Da diese sich in der Uhrzeit mit Sekundenauflösung unterscheiden, das Speichern aber wesentlich schneller abläuft als 1 Sekunde, bekommen alle Dateien in der Regel denselben Namen. Hänge doch den Seitennamen mit an den Dateinamen, dann wird es eindeutig:

Code: Alles auswählen

for i = 0  to wb.GetWorksheetCount-1 do 
  wb.WriteToFile('C:\TEMP\CSVFiles'+FormatDateTime('yyyymmdd_hhnnss', now)+'_Test_'+wb.GetWorksheetByIndex(i).Name+'.csv');

dash_develop
Beiträge: 38
Registriert: So 15. Mai 2016, 13:33

Re: fpspreedsheet - Frage

Beitrag von dash_develop »

Hallo wp_xyz,

ich habe einfach mal den Wert direkt auf 1 gesetzt und es hat funktioniert. So wie bei dir.

Code: Alles auswählen

 
    procedure TForm1.ButtonClick(Sender: TObject);
    var
      wb : TsWorkbook;
      i : Integer;
    begin
      wb := TsWorkbook.Create;
         try
            wb.ReadFromFile('C:\TEMP\ExcelFiles\Test.xlsm');
            CSVParams.SheetIndex := 1;
            wb.WriteToFile('C:\TEMP\CSVFiles\Test.csv');
            end;
         finally
            wb.Free;
         end;
    end;
 


Danke dir.

Eine Frage habe ich jedoch noch zu dem Code:

Code: Alles auswählen

 
for i = 0  to wb.GetWorksheetCount-1 do
  wb.WriteToFile('C:\TEMP\CSVFiles'+FormatDateTime('yyyymmdd_hhnnss', now)+'_Test_'+wb.GetWorksheetByIndex(i).Name+'.csv');
 


Bei dem Code bekomme ich die Benennung wie gewünscht:
DATUM_TEST_WS1.csv
DATUM_TEST_WS2.csv

Jedoch ist in beiden der Inhalt von WS1 drin.

Das verstehe ich noch nicht so ganz.

Gruß
dash_develop

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: fpspreedsheet - Frage

Beitrag von wp_xyz »

Natürlich... Man muss vorher noch den SheetIndex umschalten:

Code: Alles auswählen

for i = 0  to wb.GetWorksheetCount-1 do begin
  CSVParams.SheetIndex := i;
  wb.WriteToFile('C:\TEMP\CSVFiles'+FormatDateTime('yyyymmdd_hhnnss', now)+'_Test_'+wb.GetWorksheetByIndex(i).Name+'.csv');
end;

dash_develop
Beiträge: 38
Registriert: So 15. Mai 2016, 13:33

Re: fpspreedsheet - Frage

Beitrag von dash_develop »

Jagut klingt logisch...

Ohje shon bisschen peinlich.. Tut mir Leid. Aber vielen Dank. :lol:

Antworten