Ένα backend αναφορών σε Delphi που εξέδιδε .xlsx επί χρόνια αποκτά μια νέα απαίτηση: οι κανόνες προμηθειών ενός πελάτη δημόσιου τομέα επιβάλλουν έξοδο OpenDocument Spreadsheet, και οι αναλυτές σε εκείνον τον λογαριασμό στέλνουν πίσω τις επεξεργασίες τους ως αρχεία .ods αποθηκευμένα από το LibreOffice. Οπότε τώρα ο ίδιος κώδικας πρέπει να γράφει ODS και να το διαβάζει. Το HotXLS, η εγγενής βιβλιοθήκη υπολογιστικών φύλλων της losLab σε Object Pascal για Delphi και C++Builder, χειρίζεται και τις δύο κατευθύνσεις χωρίς καμία εγκατάσταση Excel ή LibreOffice πουθενά. Αυτό που δεν κάνει είναι να καθιστά τις δύο κατευθύνσεις συμμετρικές. Η εξαγωγή κουβαλά πολύ περισσότερα απ' όσα ανακτά η εισαγωγή, και μια ομάδα που υποθέτει το αντίθετο θα δει τύπους και μορφοποίηση να εξατμίζονται κάπου ανάμεσα στην αναθεώρηση του πελάτη και την επόμενη αναφορά, χωρίς κανένα σφάλμα να το δείξει
Η υποστήριξη ODS ζει στην πρόσοψη XLSX, όχι στην XLS
Το HotXLS διαθέτει δύο ανεξάρτητες ιεραρχίες κλάσεων σε ένα ενιαίο πακέτο: την TXLSWorkbook στη μονάδα lxHandle για δυαδικά αρχεία BIFF8 .xls, και την TXLSXWorkbook στη μονάδα lxHandleX για πακέτα OOXML .xlsx. Κάθε σημείο εισόδου OpenDocument - OpenODS, SaveAsODS, GetODSSheetNames - κρέμεται από την TXLSXWorkbook. Η τοποθέτηση δεν είναι αυθαίρετη. Ένα πακέτο ODS, όπως ορίζεται στο OASIS ODF 1.3, είναι ένα αρχείο zip που κουβαλά ένα μέλος mimetype, ένα manifest, και ένα σώμα content.xml, κάτι που το κάνει δομικό ξάδερφο του zip OOXML· το BIFF8 είναι μια δυαδική ροή εγγραφών της δεκαετίας του 1990 χωρίς κανένα κοινό σημείο
Αυτή η τοποθέτηση έχει μια πρακτική αιχμή: ένα παλαιό βιβλίο εργασίας .xls δεν μπορεί να γίνει .ods με μία κλήση. Γεφυρώνετε πρώτα το περιεχόμενο BIFF μέσα στο μοντέλο XLSX, με την SaveXLSWorkbookAsXLSX από τη μονάδα lxXlsxExport, ξανανοίγετε το αποτέλεσμα μέσω της TXLSXWorkbook, και έπειτα εξάγετε από εκεί. Η γέφυρα δεν είναι χωρίς απώλειες, και αξίζει να γνωρίζετε τα κενά πριν χτίσετε πάνω της. Αντιγράφει τιμές, τύπους, μορφές αριθμών, γραμματοσειρές, γεμίσματα, και πλάτη στηλών. Παραλείπει περιγράμματα, συγχωνευμένα εύρη, σχόλια, γραφήματα, και μορφοποίηση υπό όρους. Μια πηγή .xls με βαριά μορφοποίηση θα φτάσει στο ODS φαίνοντας πιο απλή απ' όσο έφυγε, και αυτό είναι ιδιότητα της γέφυρας, όχι του συγγραφέα ODS
Η ανίχνευση στην πλευρά εισαγωγής είναι αυτόματη. Η απλή μέθοδος Open αναγνωρίζει ένα πακέτο ODS από το μέλος mimetype του, υποχωρώντας σε έναν έλεγχο content.xml ανώτατου επιπέδου όταν εκείνο το μέλος απουσιάζει, οπότε μια γενική διαδρομή κώδικα «άνοιξε ό,τι ανέβασε ο χρήστης» δεν χρειάζεται δική της μυρωδιά επέκτασης αρχείου. Μετά το άνοιγμα, η ιδιότητα SourceFormat αναφέρει ποιος κλάδος ενεργοποιήθηκε
Εξαγωγή σε ODS με το TODSExportOptions
Η ίδια η κλήση εξαγωγής είναι μία γραμμή· το αντικείμενο επιλογών γύρω της κουβαλά τις αποφάσεις που θα ρωτήσει αργότερα ένας αναθεωρητής:
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // ο καλών κατέχει και ελευθερώνει αυτό
try
Opts.Generator := 'ReportService 4.2'; // αντικατάσταση meta:generator
Opts.IncludeCharts := True;
Opts.IncludeImages := True;
Book.SaveAsODS('quarterly-report.ods', Opts);
finally
Opts.Free;
end;
finally
Book.Free;
end;
end;
Το αντικείμενο επιλογών ανήκει στον καλούντα. Το HotXLS δεν θα το ελευθερώσει, γι' αυτό το εσωτερικό try..finally βρίσκεται εκεί και δεν είναι προαιρετικό. Οι δύο ιδιότητες που αλλάζουν την έξοδο, αντί απλώς να την επιγράφουν, αξίζουν μια πιο προσεκτική ματιά. Ο ορισμός της IncludeCharts := False κάνει περισσότερα από το να κρύψει γραφήματα: αφαιρεί τα υπο-έγγραφα γραφημάτων και τις καταχωρίσεις manifest τους από το πακέτο, κάτι που είναι ακριβώς αυτό που θέλετε όταν ο καταναλωτής είναι μια διοχέτευση δεδομένων που θα σκόνταφτε πάνω τους. Η Generator αντικαθιστά τη συμβολοσειρά ODF meta:generator, η οποία διαφορετικά διαβάζεται HotXLS/<version>· αντικαταστήστε την όταν εργαλεία παρακάτω στη ροή αναγνωρίζουν παραγωγούς αρχείων για να δρομολογήσουν υποστήριξη. Αν τίποτα από αυτά δεν ισχύει, παραλείψτε εντελώς το αντικείμενο επιλογών. Η κλήση της SaveAs(FileName, xlsxOpenDocumentSpreadsheet) είναι το ίδιο με την SaveAsODS με προεπιλογές, και οι υπερφορτώσεις stream και στις δύο σάς επιτρέπουν να γράψετε το πακέτο απευθείας σε μια απόκριση HTTP χωρίς προσωρινό αρχείο
Τι διαβάζει η διαδρομή εισαγωγής - και τι σκόπιμα παραλείπει
Διαβάστε αυτό το κομμάτι προσεκτικά πριν υποσχεθείτε σε κανέναν πιστότητα κύκλου ανάγνωσης-εγγραφής. Η εισαγωγή ODS στο HotXLS είναι σκόπιμα μια ελαφριά διαδρομή. Διατηρεί βαθμωτές τιμές κελιών και το αποθηκευμένο αποτέλεσμα που κουβαλούσε κάθε τύπος τη στιγμή της αποθήκευσης, και επεκτείνει επαναλαμβανόμενες γραμμές και στήλες μέσα στο πλέγμα. Δεν μεταφέρει στιλ, εκφράσεις τύπων ODS, ή σχέδια
Η επιλογή σχετικά με τους τύπους είναι αυτή που έχει τις περισσότερες πιθανότητες να δαγκώσει, και έγινε σκόπιμα. Ένα κελί ODF αποθηκεύει δύο πράγματα δίπλα δίπλα: την έκφραση τύπου, γραμμένη στη διάλεκτο OpenFormula που ορίζεται στο ODF 1.3 Μέρος 4, και την τελευταία τιμή που υπολόγισε γι' αυτήν η εφαρμογή παραγωγής. Η μετάφραση του OpenFormula σε σύνταξη τύπου Excel είναι το δικό της πρόβλημα μετατροπής διαλέκτου, με πραγματικές οριακές περιπτώσεις γύρω από λεξιλόγια συναρτήσεων, σύνταξη αναφορών, και μοντέλα σφαλμάτων. Η ανάγνωση της αποθηκευμένης τιμής αντ' αυτού παρακάμπτει ολόκληρη αυτή την κατηγορία σιωπηλής εσφαλμένης μετάφρασης, οπότε οι αριθμοί που εισάγετε είναι ακριβώς οι αριθμοί που είδε τελευταία ο αποστολέας. Το κόστος είναι ότι φτάνουν ως αριθμοί, όχι ως οι ζωντανοί τύποι που τους παρήγαγαν
Ο τρόπος αστοχίας γύρω από τον οποίο πρέπει να σχεδιάσετε προκύπτει άμεσα: ένα υπολογιστικό φύλλο του οποίου τα σύνολα ήταν σωστά όταν το αποθήκευσε τελευταία το LibreOffice εισάγεται με σωστούς αριθμούς, αλλά αυτοί οι αριθμοί είναι τώρα σταθερές. Επεξεργαστείτε ένα κελί εισόδου, επανυπολογίστε, και τίποτα δεν κινείται - ο τύπος έχει φύγει, μόνο το τελικό του αποτέλεσμα παραμένει. Αν η ροή εργασίας χρειάζεται ζωντανούς τύπους μετά την εισαγωγή, επανεγκαταστήστε τους προγραμματιστικά από τους δικούς σας επιχειρηματικούς κανόνες μέσω της Cell.Formula, η οποία στην πρόσοψη XLSX δέχεται την έκφραση χωρίς αρχικό σύμβολο ίσον
Σχεδιάζοντας γύρω από τον ασύμμετρο κύκλο ανάγνωσης-εγγραφής
Η εξαγωγή αποδίδει από το πλήρες εν μνήμη μοντέλο βιβλίου εργασίας: τιμές, στιλ, και, αν τα ζητήσετε, γραφήματα και εικόνες. Η εισαγωγή επιστρέφει μόνο τιμές. Οπότε το σκέλος .xlsx προς .ods είναι υψηλής πιστότητας, και το σκέλος .ods προς .xlsx φέρνει πίσω τιμές και αποθηκευμένα αποτελέσματα αλλά όχι στιλ και όχι ζωντανούς τύπους. Αλυσιδώστε τα δύο και η ασυμμετρία πολλαπλασιάζεται. Ένας πλήρης κύκλος .xlsx προς .ods προς .xlsx γράφει τα πάντα πιστά στην έξοδο και χάνει τα στιλ και τους τύπους στην επιστροφή, ακόμη κι αν τίποτα δεν πήγε στραβά σε κανένα από τα δύο βήματα
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // η μορφή ανιχνεύεται αυτόματα
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Οι τιμές και τα αποθηκευμένα αποτελέσματα τύπων είναι παρόντα μετά από
// μια εισαγωγή ODS· τα στιλ και οι ζωντανοί τύποι όχι. Ξαναχτίστε ό,τι
// χρειάζεται η διοχέτευση παρακάτω στη ροή πριν την αποθήκευση.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Το αρχιτεκτονικό μοτίβο που προκύπτει από αυτό: αντιμετωπίστε τα εισερχόμενα αρχεία .ods ως τροφοδοσίες δεδομένων, όχι ως έγγραφα προς επεξεργασία επί τόπου. Κρατήστε το κανονικό βιβλίο εργασίας σε .xlsx, διαβάστε τιμές από τις αναθεωρήσεις πελατών, και εκδώστε φρέσκο ODS κατ' απαίτηση από το κανονικό αντίγραφο. Η επαλήθευση ανήκει και στα δύο στρατόπεδα - ανοίξτε εξαγόμενα αρχεία στο LibreOffice Calc, τον καταναλωτή αναφοράς ODF, και στο Excel, που διαβάζει ODS επί χρόνια αλλά διαφωνεί με το LibreOffice στα άκρα της υποστήριξης γραφημάτων και στιλ. Ο αριθμός φύλλων, μια χούφτα βασικά κελιά, και η παρουσία γραφημάτων αποτελούν έναν επαρκή smoke check ανά προφίλ εξαγωγής
Διαλογή ενός αρχείου ODS πριν δεσμευτείτε σε μια εισαγωγή
Όταν ένα endpoint δέχεται uploads, η απαρίθμηση ονομάτων φύλλων είναι πολύ φθηνότερη από μια πλήρη ανάλυση και πιάνει δομικές εκπλήξεις νωρίς:
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetODSSheetNames('incoming.ods', Names) <= 0 then
raise Exception.Create('not a readable ODS package');
if Names.IndexOf('Data') < 0 then
raise Exception.Create('revision is missing the Data sheet');
finally
Book.Free;
Names.Free;
end;
Η σύμβαση επιστροφής μπερδεύει τον κόσμο: οι κλήσεις του HotXLS γενικά επιστρέφουν έναν θετικό αριθμό ή 1 σε επιτυχία και -1 σε αποτυχία, αδειάζοντας τη λίστα καθώς αποτυγχάνουν, οπότε ελέγξτε <= 0 αντί να συγκρίνετε με μία συγκεκριμένη θετική τιμή. Η GetODSSheetNames ούτε επαναφέρει ούτε γεμίζει την παρουσία βιβλίου εργασίας, οπότε ένα μοναδικό αντικείμενο εξέτασης μπορεί να ελέγξει έναν ολόκληρο φάκελο εισερχόμενων αρχείων. Δομικοί έλεγχοι σαν αυτόν πιάνουν τη συχνότερη αστοχία στον πραγματικό κόσμο - έναν αναλυτή που μετονομάζει ή διαγράφει ένα φύλλο πριν στείλει πίσω την αναθεώρηση - στην πύλη, όπου το μήνυμα σφάλματος μπορεί ακόμη να κατονομάσει το αρχείο και το φύλλο που λείπει αντί να αναδυθεί ως αναφορά nil τρία επίπεδα βαθύτερα
Αν χτίζετε μια ευρύτερη διοχέτευση μετατροπής γύρω από αυτό, το μοτίβο workbench ελέγχου και μετατροπής βιβλίου εργασίας δείχνει πώς να απογράψετε τα χαρακτηριστικά ενός αρχείου πριν επιλέξετε μια μορφή στόχο, και ο οδηγός απόδοσης μεγάλων βιβλίων εργασίας κρατά τις μαζικές εξαγωγές μέσα σε λογικά όρια μνήμης
Το HotXLS είναι μια εγγενής βιβλιοθήκη υπολογιστικών φύλλων για Delphi και C++Builder με πλήρη πηγαίο κώδικα· η πλήρης λίστα χαρακτηριστικών και οι λεπτομέρειες αδειοδότησης βρίσκονται στη σελίδα προϊόντος HotXLS Delphi Component