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

Δομημένες Αναφορές Πίνακα Excel στο Delphi με HotXLS

Το HotXLS πλέον υπολογίζει δομημένες αναφορές πίνακα, οπότε το =SUM(Table1[Amount]) παράγει έναν αριθμό αντί να παραλείπεται. Ο resolver χειρίζεται τα Table[Column], Table[[Column]], εύρη στηλών όπως Table[[Q1]:[Q4]], και τους item specifiers [#Data], [#All], [#Headers] και [#Totals], επιλύοντας το καθένα έναντι του μοντέλου πίνακα του βιβλίου εργασίας κατά την ανάλυση, ενώ το αρχικό κείμενο φόρμουλας κάνει round-trip αυτούσιο

Μία μορφή απουσιάζει σκόπιμα, και είναι αυτή που ο κόσμος συναντά πρώτη. Η συντόμευση τρέχουσας γραμμής [@Column] δεν υποστηρίζεται, για δομικό λόγο άξιο κατανόησης αντί για τυφλή παράκαμψη

Γιατί μια δομημένη αναφορά δεν είναι απλώς ένα εύρος με φιλικό όνομα;

Επειδή ένα ονομαστικό εύρος παγώνει μια διεύθυνση και μια αναφορά πίνακα όχι. Γράψτε DataBlock ως όνομα που δείχνει στο Sheet1!$A$2:$D$100 και παραμένει αυτό το ορθογώνιο μέχρι κάτι να το ξαναγράψει. Γράψτε Sales[Amount] και σημαίνει "η στήλη Amount του πίνακα Sales", όποια κι αν είναι η έκταση αυτού του πίνακα τη στιγμή που η φόρμουλα υπολογίζεται. Προσθέστε είκοσι γραμμές στον πίνακα και το άθροισμα τις καλύπτει· δεν υπάρχει αναφορά προς προσαρμογή επειδή δεν υπήρξε ποτέ διεύθυνση στη φόρμουλα εξαρχής

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

Η γραμματική που επιλύει το HotXLS

Η υποστηριζόμενη γραμματική specs καλύπτει ένα μοναδικό ορθογώνιο αποτέλεσμα και αξίζει να δηλωθεί με ακρίβεια, επειδή η τεκμηρίωση του Excel παρουσιάζει μια πολύ μεγαλύτερη επιφάνεια απ' όσο υλοποιούν οι περισσότερες μηχανές. Το HotXLS δέχεται [Col] και την παρενθετική παραλλαγή [[Col]], τους γυμνούς item specifiers [#Data], [#All], [#Headers] και [#Totals], τη συνδυασμένη μορφή [[#Data],[Col]], ένα εύρος μέσα σε έναν item specifier ως [[#Data],[Col1]:[Col2]], και ένα απλό εύρος [Col1]:[Col2]

Αυτό το σύνολο σας δίνει κάθε σχήμα αναφοράς που παράγει ένα συνεχόμενο μπλοκ: μια στήλη, μια σειρά γειτονικών στηλών, ένα κομμάτι μόνο σώματος ή με κεφαλίδα του καθενός. Μη γειτονικές ενώσεις και αποτελέσματα πολλαπλών περιοχών βρίσκονται εκτός αυτού. Όταν μια αναφορά δεν μπορεί να επιλυθεί, η φόρμουλα διατηρεί την προηγούμενη συμπεριφορά παράλειψης-χωρίς-τιμή αντί να αντικαθιστά μια εικασία, οπότε μια ανεπίλυτη αναφορά δεν γίνεται ποτέ ένας εύλογα λανθασμένος αριθμός

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Cols: TStringList;
begin
  Book := TXLSXWorkbook.Create;
  Cols := TStringList.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Cols.Add('Region');
    Cols.Add('Q1');
    Cols.Add('Q2');
    Cols.Add('Amount');
    Sheet.Tables.Add('SalesTable', 'A1:D25', Cols);
    // ... γράψτε τη γραμμή κεφαλίδας και τις 24 γραμμές δεδομένων ...

    Sheet.Cells[27, 4].Formula := 'SUM(SalesTable[Amount])';
    Sheet.Cells[28, 4].Formula := 'SUM(SalesTable[[Q1]:[Q2]])';
    Sheet.Cells[29, 4].Formula := 'COUNTA(SalesTable[[#Data],[Region]])';
    Sheet.Cells[30, 4].Formula := 'ROWS(SalesTable[#All])';

    Book.Recalculate;
    Book.SaveAs('sales.xlsx');
  finally
    Cols.Free;
    Book.Free;
  end;
end;

Γιατί η μορφή τρέχουσας γραμμής αποκλείεται σκόπιμα;

Τα [@Column] και [#This Row] σημαίνουν "το κελί εκείνης της στήλης στη γραμμή όπου ζει αυτή η φόρμουλα". Η τιμή επομένως εξαρτάται από τη θέση του κελιού που υπολογίζει, όχι μόνο από τον πίνακα. Αυτό είναι διαφορετικό είδος αναφοράς: όχι ένα ορθογώνιο που ο compiler μπορεί να επιλύσει μία φορά, αλλά μια επίλυση ανά κελί που πρέπει να επαναληφθεί για κάθε γραμμή που καταλαμβάνει η φόρμουλα

Το HotXLS επιστρέφει False από τον resolver εύρους πίνακα για αυτές τις μορφές, κάτι που τις δρομολογεί στη διαδρομή παράλειψης-χωρίς-τιμή. Το κείμενο φόρμουλας διατηρείται και γράφεται πίσω αμετάβλητο, οπότε ένα βιβλίο εργασίας που χρησιμοποιεί [@Amount] ανοίγει σωστά στο Excel μετά από round-trip μέσα από την εφαρμογή σας· απουσιάζει μόνο η τιμή που θα υπολόγιζε το HotXLS. Έχοντας την επιλογή ανάμεσα σε απούσα τιμή και τιμή υπολογισμένη έναντι λάθος γραμμής, η απουσία είναι αυτή που μπορείτε να εντοπίσετε

Η πρακτική παράκαμψη είναι μηχανική: σε ένα βιβλίο εργασίας που παράγετε, γράψτε την ισοδύναμη σχετική αναφορά A1-style, κάτι που το Excel αποθηκεύει εσωτερικά ούτως ή άλλως για πολλή λογική εμβέλειας πίνακα. Σε ένα βιβλίο εργασίας που απλώς επεξεργάζεστε, αφήστε τη φόρμουλα ως έχει και διαβάστε την cached τιμή που το Excel ήδη αποθήκευσε, κάτι που συνήθως θέλει ένα pipeline φόρτωσης και αναφοράς

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Table: TXLSXTable;
  Row: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('sales.xlsx') <> 1 then Exit;
    Sheet := Book.Sheets[1];

    Table := Sheet.Tables.FindByName('SalesTable');
    if Table <> nil then
    begin
      // Αναζήτηση στιλ recordset πάνω στο σώμα του πίνακα, αποτέλεσμα γραμμής βάσει 1
      Row := Table.FindFirst(Sheet, 'Region', 'EMEA');
      while Row > 0 do
      begin
        Log(VarToStr(Sheet.Cells[Row, 4].Value));
        Row := Table.FindNext(Sheet, 'Region', 'EMEA', Row);
      end;
    end;
  finally
    Book.Free;
  end;
end;

Τι συμβαίνει όταν ο πίνακας αλλάζει σχήμα

Οι δομημένες αναφορές ακυρώνονται αντί να επανακατευθύνονται σιωπηλά όταν αυτό που ονομάζουν εξαφανίζεται. Διαγράψτε μια στήλη και οι φόρμουλες που αναφέρονται σε αυτή τη στήλη ακυρώνονται όπως τις ακυρώνει το Excel· διαγράψτε ή μετονομάστε τον πίνακα και οι αναφορές σε αυτόν αντιμετωπίζονται με τον ίδιο τρόπο. Αυτή είναι η σωστή συμπεριφορά και αντικατοπτρίζει την κοινή προσαρμογή αναφορών, που περιγράφεται στο προσαρμογή αναφορών φόρμουλας κατά εισαγωγή και διαγραφή, όπου η δουλειά της μηχανής είναι να κρατά τις φόρμουλες τίμιες αντί να τις κρατά απλώς εμφανισιακά έγκυρες

Η αύξηση γραμμών είναι η αντίθετη περίπτωση και δεν χρειάζεται καμία προσαρμογή. Επειδή η αναφορά ονομάζει τον πίνακα και όχι ένα ορθογώνιο, η προσάρτηση γραμμών μέσα στο εύρος του πίνακα διευρύνει ό,τι καλύπτει το [#Data] χωρίς να αγγίξει καμία φόρμουλα. Αυτή είναι η ιδιότητα που κάνει τους πίνακες άξιους χρήσης σε ένα πρότυπο αναφοράς: η γραμμή συνόλων συνεχίζει να αθροίζει ό,τι παρήγαγε η εισαγωγή, όσες γραμμές κι αν αποδείχθηκε ότι ήταν

Πειθαρχία round-trip

Το HotXLS διατηρεί το αρχικό κείμενο φόρμουλας. Ένα βιβλίο εργασίας φορτωμένο με SUM(SalesTable[Amount]) αποθηκεύεται με SUM(SalesTable[Amount]), όχι με το επιλυμένο SUM(D2:D25). Αυτό έχει περισσότερη σημασία απ' όσο φαίνεται: ένας χρήστης που ανοίγει την έξοδό σας στο Excel περιμένει να δει τη φόρμουλα που έγραψε, και μια επιλυμένη διεύθυνση θα μετέτρεπε σιωπηλά ένα αυτο-συντηρούμενο μοντέλο σε ένα εύθραυστο που σταματά να καλύπτει νέες γραμμές

Δύο σχετικές δυνατότητες συμπληρώνουν την εικόνα. Οι ίδιοι οι ορισμοί πινάκων, συμπεριλαμβανομένων πινάκων χωρίς κεφαλίδα και σχολίων ανά πίνακα, κάνουν round-trip μέσα από το μοντέλο πίνακα που περιγράφεται στο επικύρωση δεδομένων, AutoFilter και πίνακες Excel. Και όταν πολλά κελιά μοιράζονται ένα μοτίβο, το XLSX τα αποθηκεύει μία φορά ως κοινή φόρμουλα, η οποία επεκτείνεται και επανεκπέμπεται όπως καλύπτεται στο επέκταση si κοινής φόρμουλας. Δομημένες αναφορές μέσα σε κοινές φόρμουλες περνούν από τις δύο διαδρομές, οπότε πρέπει και οι δύο να συμπεριφέρονται σωστά, και συμπεριφέρονται

Το HotXLS διαβάζει και γράφει XLS, XLSX και ODS από Delphi και C++Builder χωρίς εγκατεστημένο Excel και χωρίς αυτοματισμό Office, υπολογίζοντας φόρμουλες στη δική του μηχανή. Το μοντέλο πίνακα, η μηχανή φορμουλών και το API επανυπολογισμού τεκμηριώνονται στη σελίδα του HotXLS Delphi spreadsheet component