Τεχνικό Άρθρο

Ορισμός ιδιοτήτων εγγράφου Excel και μεταδεδομένων βιβλίου εργασίας στο Delphi με το HotXLS

Ένα υπολογιστικό φύλλο φέρει δύο επίπεδα ταυτότητας. Υπάρχει το πλέγμα κελιών, και υπάρχουν τα μεταδεδομένα εγγράφου που ταξιδεύουν μαζί του: τίτλος, συγγραφέας, εταιρεία, λέξεις-κλειδιά, οι χρονικές σφραγίδες. Το Excel ποτέ δεν δείχνει αυτό το δεύτερο επίπεδο μέσα στο πλέγμα, κι όμως είναι το επίπεδο που ευρετηριάζει το Windows Search, αυτό που διαβάζει το SharePoint για να τιτλοφορήσει ένα έγγραφο, και αυτό με το οποίο αρχειοθετεί ένα σύστημα διαχείρισης αρχείων. Όταν ένα παραγόμενο βιβλίο εργασίας κληρονομεί τον Author και τον Title του από το πρότυπο από το οποίο χτίστηκε, κάθε σύστημα παρακάτω καταγράφει τον σχεδιαστή του προτύπου ως συγγραφέα τεσσάρων χιλιάδων καταστάσεων λογαριασμού πελατών. Τα μεταδεδομένα δεν είναι σωστά πουθενά και συμβουλεύονται παντού

Το HotXLS εκθέτει αυτό το επίπεδο ως συνηθισμένες ιδιότητες σε επίπεδο βιβλίου εργασίας και στις δύο μηχανές του: την πρόσοψη BIFF για .xls και την πρόσοψη OOXML για .xlsx. Διαβάζετε ένα πεδίο αφού ανοίξετε ένα αρχείο και γράφετε ένα πεδίο πριν το αποθηκεύσετε. Η βιβλιοθήκη αποφασίζει σε ποιο φυσικό container καταλήγει η τιμή. Αυτό που αξίζει να κατανοήσετε πριν γράψετε μια γεννήτρια είναι ποια πεδία υποστηρίζει πραγματικά κάθε μορφή, πού ζουν φυσικά αυτά τα πεδία, και ο ένας κανόνας πύλης που καθορίζει αν ένα .xlsx καταγράφει καθόλου μεταδεδομένα

Δύο μορφές, δύο μοντέλα αποθήκευσης

Ο λόγος που μια βιβλιοθήκη υπολογιστικών φύλλων χρειάζεται δύο υλοποιήσεις μεταδεδομένων, και ο λόγος που μισοτελειωμένα εργαλεία σφραγίζουν σωστά τη μία μορφή και ξεχνούν την άλλη, είναι ότι τα .xls και .xlsx κρατούν τις ιδιότητές τους σε άσχετα μεταξύ τους μέρη. Ένα βιβλίο εργασίας BIFF τις γράφει σε ροές αρχείου σύνθετου τύπου OLE, κυρίως στο σύνολο ιδιοτήτων SummaryInformation που προϋπάρχει του ίδιου του Excel, μαζί με την εγγραφή WRITEACCESS μέσα στη ροή που ονομάζει όποιον αποθήκευσε τελευταίος το αρχείο. Ένα βιβλίο εργασίας OOXML τις κρατά ως τμήματα XML μέσα στο πακέτο zip, χωρισμένα ανά σκοπό: το docProps/core.xml κρατά τα πεδία Dublin Core (τίτλος, δημιουργός, θέμα, λέξεις-κλειδιά, ημερομηνίες) και το docProps/app.xml κρατά τα πεδία σε επίπεδο εφαρμογής όπως η εταιρεία και η εφαρμογή δημιουργίας, σύμφωνα με το ECMA-376 Part 1

Το HotXLS ισοπεδώνει και τα δύο αυτά μοντέλα αποθήκευσης σε άμεσες ιδιότητες του αντικειμένου βιβλίου εργασίας. Ποτέ δεν ανοίγετε μια ροή συνόλου ιδιοτήτων ούτε επεξεργάζεστε ένα τμήμα XML με το χέρι. Αναθέτετε συμβολοσειρές και ημερομηνίες στο βιβλίο εργασίας, και το σωστό container υλοποιείται για όποια μορφή αποθηκεύετε

Διάγραμμα HotXLS Delphi που συγκρίνει την αποθήκευση SummaryInformation BIFF με τα μέρη docProps OOXML για ιδιότητες εγγράφων Excel
Το HotXLS επιπεδώνει δύο άσχετα μοντέλα αποθήκευσης σε μία επιφάνεια ιδιοτήτων βιβλίου εργασίας — η μηχανή διαλέγει τον φυσικό περιέκτη όταν το αρχείο αποθηκεύεται

Σφράγιση παραγόμενων βιβλίων εργασίας από την επιχειρηματική εγγραφή

Στην πλευρά XLSX, η TXLSXWorkbook εκθέτει τις Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application και AppVersion ως συμβολοσειρές, καθώς και τις Created και Modified ως τιμές TDateTime όπου μηδέν σημαίνει μη ορισμένο. Ο κανόνας που κλείνει την τρύπα κληρονομικότητας είναι μία πρόταση: αναθέστε κάθε πεδίο σε κάθε εκτέλεση, παίρνοντας τις τιμές από την επιχειρηματική εγγραφή αντί να εμπιστεύεστε ό,τι έτυχε να κρατά το πρότυπο

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('statement-template.xlsx') <> 1 then
      raise Exception.Create('Template not available');

    // Αντικαταστήστε κάθε πεδίο: ό,τι μείνει ανέγγιχτο
    // κληρονομείται από όποιον σχεδίασε το πρότυπο.
    Book.Title := 'Account Statement 2026-06 / ACME Corp';
    Book.Subject := 'Monthly account statement';
    Book.Author := 'Billing Service 4.2';
    Book.LastModifiedBy := 'Billing Service 4.2';
    Book.Company := 'Northwind Financial';
    Book.Category := 'Customer Delivery';
    Book.Keywords := 'statement;billing;2026-06;acct-10024';
    Book.Description := 'Generated document - manual edits are not retained';
    Book.Created := Now;
    Book.Modified := Now;

    Book.SaveAs('statement-10024.xlsx');
  finally
    Book.Free;
  end;
end;

Το πεδίο Keywords ανταποδίδει περισσότερη σκέψη απ' όση συνήθως παίρνει. Η υποδομή αναζήτησης το ευρετηριάζει αυτούσιο, το Windows Search, το SharePoint και τα περισσότερα προϊόντα DMS το ίδιο, οπότε μια σύμβαση χωρισμένη με ερωτηματικό που φέρει τον αριθμό λογαριασμού και την περίοδο μετατρέπει κάθε παραδιδόμενο βιβλίο εργασίας σε μια ευρέσιμη εγγραφή χωρίς κανένα round trip σε βάση δεδομένων. Η ίδια εμβέλεια είναι και η παγίδα. Οι ιδιότητες ταξιδεύουν με κάθε αντίγραφο του αρχείου, πολύ πέρα από τους ελέγχους πρόσβασης του συστήματος που τις έγραψε, οπότε προσωπικά δεδομένα δεν έχουν θέση εκεί

Το ζεύγος χρονικών σφραγίδων φέρει σημασιολογία που αξίζει να καθοριστεί σε πολιτική αντί να αφεθεί στη συνήθεια. Η Created θα πρέπει να σημαδεύει τη στιγμή που το pipeline σας παρήγαγε το έγγραφο και μετά να παραμένει παγωμένη. Η Modified είναι το πεδίο που ενημερώνει το Excel όποτε ένας παραλήπτης αποθηκεύει το αρχείο, οπότε μια απόκλιση ανάμεσα στις δύο μετά την παράδοση είναι θετική απόδειξη ότι κάποιος επεξεργάστηκε το βιβλίο εργασίας παρακάτω, κάτι που διευθετεί περισσότερες από μία διαφωνίες για το ποιανού είναι στην πραγματικότητα οι αριθμοί ενός προωθημένου υπολογιστικού φύλλου. Μια παγίδα κρύβεται στην κατάσταση μη ορισμένου: είναι η κυριολεκτική τιμή μηδέν, όχι εξαίρεση και όχι null, οπότε ο κώδικας ελέγχου πρέπει να ελέγχει ρητά για μηδέν. Μορφοποιήστε ένα μη ορισμένο TDateTime χωρίς αυτή τη διασφάλιση και τα logs σας θα γεμίσουν με μια σίγουρη αλλά λάθος ημερομηνία Δεκεμβρίου 1899

DocPropsTouched: το βιβλίο εργασίας που παραδίδεται χωρίς docProps

Μια σημαία μόνο για ανάγνωση, η DocPropsTouched, ελέγχει τον writer ιδιοτήτων XLSX. Ένα βιβλίο εργασίας στο οποίο δεν ανατέθηκε ποτέ καμία ιδιότητα δεν παράγει κανένα τμήμα docProps· το HotXLS αρνείται να γράψει έναν άδειο σκελετό μεταδεδομένων. Η συμπεριφορά είναι τακτοποιημένη, και έχει δύο συνέπειες που αξίζει να σχεδιάσετε γύρω τους

Ο κώδικας εισαγωγής στην πλευρά που καταναλώνει δεν πρέπει να υποθέτει ότι το core.xml υπάρχει σε κάθε πακέτο. Ένα εργαλείο που το απαιτεί άκαμπτα θα απορρίψει απόλυτα έγκυρα ελάχιστα αρχεία. Και αν η στάση συμμόρφωσής σας απαιτεί κάθε εξερχόμενο έγγραφο να φέρει τουλάχιστον μια ταυτότητα γεννήτριας, αυτή η απαίτηση γίνεται κώδικας και όχι ιδιότητα της μορφής: αναθέστε τις Application και Author ανεξαρτήτως συνθηκών στη διαδρομή αποθήκευσης, αφού ένα ανέγγιχτο βιβλίο εργασίας είναι εντελώς νόμιμο σύμφωνα με την προδιαγραφή ενώ παραβιάζει αθόρυβα την πολιτική σας

Διάγραμμα ροής HotXLS Delphi που δείχνει τη σημαία DocPropsTouched να λειτουργεί ως πύλη για την έξοδο docProps σε αποθηκευμένα βιβλία εργασίας XLSX
Το DocPropsTouched πυλώνει τον writer docProps του XLSX — αναθέστε Application και Author άνευ όρων όταν η πολιτική απαιτεί ταυτότητα δημιουργού

Η παλαιού τύπου επιφάνεια XLS και η παγίδα Comments

Η πρόσοψη BIFF φέρει το παλαιότερο, μικρότερο σύνολο πεδίων: Title, Subject, Author, Keywords, Comments, Company και Manager, καθώς και την LastSavedBy, ένα ψευδώνυμο της UserName, η οποία γράφει την εγγραφή WRITEACCESS που εμφανίζει το Excel όταν κάποιος άλλος χρήστης έχει κλειδωμένο το αρχείο

var
  Legacy: IXLSWorkbook;     // interface που μετρά αναφορές: χωρίς χειροκίνητο Free
begin
  Legacy := TXLSWorkbook.Create;
  if Legacy.Open('archive-1999.xls') <= 0 then
    raise Exception.Create('Cannot open archive file');

  Legacy.Title := 'FY1999 ledger (migrated copy)';
  Legacy.Author := 'Archive Migration Batch';
  Legacy.Company := 'Northwind Financial';
  Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
  Legacy.LastSavedBy := 'migration-svc';   // εγγραφή BIFF WRITEACCESS

  Legacy.SaveAs('archive-1999-stamped.xls');
end;

Μια σύγκρουση ονομασίας προκαλεί επαναλαμβανόμενη σύγχυση. Η ιδιότητα Comments σε επίπεδο εγγράφου εδώ είναι η παρατήρηση ελεύθερου κειμένου που εμφανίζεται στον διάλογο ιδιοτήτων του αρχείου. Δεν έχει καμία σχέση με τα σχόλια κελιών, που είναι αντικείμενα επιπέδου σχεδίασης προσαρτημένα σε εύρη μέσω ενός εντελώς ξεχωριστού API. Μια αναθεώρηση κώδικα που δέχεται «ήδη γράφουμε Comments» χωρίς να ελέγξει ποιο από τα δύο εννοείται έχει δεχτεί έναν ισχυρισμό για το λάθος χαρακτηριστικό, και αυτό συμβαίνει πιο συχνά απ' όσο θα υπέθετε κανείς από το κοινό όνομα. Τα δύο μοιράζονται τέσσερα γράμματα και ούτε ένα byte αποθήκευσης

Ανάγνωση μεταδεδομένων κατά την εισαγωγή, και το κενό εξέτασης

Η ανάγνωση είναι συμμετρική. Μετά την Open, οι ίδιες ιδιότητες επιστρέφουν γεμάτες από το αρχείο, κάτι που μετατρέπει έναν έλεγχο μεταδεδομένων εισερχόμενων βιβλίων εργασίας σε έναν σύντομο βρόχο

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) = 1 then
    begin
      Writeln(Format('%s | title="%s" author="%s" created=%s',
        [ExtractFileName(FileName), Book.Title, Book.Author,
         FormatDateTime('yyyy-mm-dd', Book.Created)]));
      if Book.Created = 0 then
        Writeln('  no creation date recorded');
    end;
  finally
    Book.Free;
  end;
end;

Σχεδιάστε γύρω από έναν περιορισμό ενώ το κάνετε αυτό. Δεν υπάρχει εξέταση μόνο για ιδιότητες. Η GetSheetNames μπορεί να παραθέσει φύλλα χωρίς να φορτώσει ένα βιβλίο εργασίας, αλλά η ανάγνωση της Title ή της Author σημαίνει πλήρη Open, οπότε η διαλογή μεταδεδομένων σε ένα μεγάλο αρχείο πληρώνει το πλήρες κόστος ανάλυσης σε κάθε αρχείο. Στην πλευρά BIFF μπορείτε να περικόψετε αυτό το κόστος για ελέγχους μόνο ανάγνωσης ορίζοντας την _DisableGraphics σε true πριν από το άνοιγμα, κάτι που παρακάμπτει εντελώς το επίπεδο σχεδίασης. Ταιριάζει σε έναν βρόχο που διαβάζει μόνο ιδιότητες και στατιστικά κελιών, και είναι ακριβώς λάθος τη στιγμή που το ίδιο instance μπορεί να αποθηκεύσει, επειδή το παρακαμφθέν περιεχόμενο σχεδίασης θα χανόταν. Όταν μόνο η δομή φύλλων μπορεί να προφιλτράρει το σύνολο, με τις εξαγωγές ενός φύλλου να είναι το προφανές πράγμα προς παράλειψη, οι φθηνές τεχνικές στο άρθρο μας για τον κατάλογο φύλλων και την ελαφριά επιθεώρηση μειώνουν πόσα αρχεία φτάνουν στο ακριβό πέρασμα. Και σε δουλειές μαζικής σφράγισης, όπου γράφονται χιλιάδες έξοδοι αντί να επιθεωρούνται, τα μοτίβα ρυθμού εγγραφής στο άρθρο μας για streaming εγγραφές σε δουλειές παρτίδας μεταφέρονται χωρίς αλλαγή, αφού η ανάθεση ιδιοτήτων δεν προσθέτει τίποτα μετρήσιμο στον χρόνο αποθήκευσης

Διάβαση μεταξύ μορφών και περιορισμός της διαρροής

Οι ιδιότητες κάνουν round-trip καθαρά μέσα σε μία μόνο πρόσοψη: ανοίξτε ένα .xlsx, επεξεργαστείτε το, αποθηκεύστε το, και το σύνολο επιστρέφει άθικτο. Η διάβαση μεταξύ μορφών είναι εκεί όπου σπάει η υπόθεση της ισοτιμίας, επειδή τα σύνολα πεδίων BIFF και OOXML δεν ευθυγραμμίζονται ένα προς ένα. Το BIFF έχει Manager και καμία χρονική σφραγίδα· το OOXML έχει Category, Description, και το ζεύγος Created/Modified. Ένας μετατροπέας που αντιγράφει τυφλά χάνει ό,τι δεν μπορεί να κρατήσει η μορφή προορισμού, οπότε αντιστοιχίστε τα πεδία ρητά και βάλτε την αντιστοίχιση στη λίστα ελέγχου μετατροπής σας δίπλα σε ό,τι άλλο δεν επιβιώνει το ταξίδι

Χάρτης πεδίων HotXLS Delphi που δείχνει ποιες ιδιότητες εγγράφων Excel επιβιώνουν από μετατροπή δια-μορφωτικού τύπου μεταξύ XLS και XLSX
Μια τυφλή αντιγραφή μεταξύ μορφών ρίχνει κάθε πεδίο που ο προορισμός δεν μπορεί να κρατήσει — χαρτογραφήστε ρητά τα σύνολα ιδιοτήτων BIFF και OOXML στη λίστα ελέγχου μετατροπής

Η διαρροή που ανοίγει η κληρονομικότητα προτύπου πηγαίνει προς την αντίθετη κατεύθυνση: πληροφορίες που ποτέ δεν σκοπεύατε να στείλετε προς τα έξω. Ονόματα συγγραφέων, εσωτερικές ετικέτες έργου παρκαρισμένες στις λέξεις-κλειδιά, έναν πρόχειρο τίτλο που κανείς δεν ενέκρινε. Η πειθαρχία αντικατάστασης-των-πάντων από τη γεννήτρια παραπάνω είναι όλη η άμυνα, και αξίζει να επαληθευτεί όπως θα το έκανε ένας εξωτερικός, ανοίγοντας τον διάλογο Ιδιότητες που μπορεί να προσπελάσει κάθε πελάτης ή αποσυμπιέζοντας το .xlsx και διαβάζοντας το docProps/core.xml κατευθείαν από το πακέτο. Αυτό που βλέπετε εκεί είναι ακριβώς αυτό που βλέπει κάθε indexer παρακάτω

Αυτή η ορατότητα παρακάτω είναι επίσης ο λόγος που μερικά πεδία κερδίζουν περισσότερη προσοχή απ' τα υπόλοιπα. Οι Title, Author, Keywords (που εμφανίζονται ως Tags), και Comments ή Description φέρουν το μεγαλύτερο μέρος του βάρους ευρετηρίασης στο SharePoint και στο Windows Search. Ένας Title που είναι γνήσια ξεχωριστός ανά έγγραφο, φέρνοντας την περίοδο και τον λογαριασμό, κάνει περισσότερα για την ευρεσιμότητα από οποιοδήποτε σχήμα ονομασίας φακέλων στοιβαγμένο από πάνω του, και κοστίζει μία ανάθεση ανά αποθήκευση

Οι ιδιότητες εγγράφου είναι το φθηνότερο επαγγελματικό φινίρισμα που μπορεί να φέρει ένα παραγόμενο βιβλίο εργασίας, και το πιο συχνά παραδιδόμενο ελάττωμα όταν κανείς δεν τις κατέχει. Και οι δύο επιφάνειες ιδιοτήτων που περιγράφονται εδώ ανήκουν στο HotXLS Delphi Component, το οποίο τις γράφει εγγενώς για XLS και XLSX χωρίς αυτοματισμό Excel