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

Wildcards Excel στο HotXLS: COUNTIF, MATCH, DSUM, Find

Το HotXLS Delphi Component διαβάζει το ίδιο pattern string με τέσσερις διαφορετικούς τρόπους, επειδή έτσι κάνει η Excel 16. Στο COUNTIF και το SUMIF το κείμενο a~b είναι κυριολεκτικό εκτός αν το κριτήριο περιέχει επίσης * ή ?· σε MATCH και XLOOKUP wildcard mode η tilde είναι πάντα escape, οπότε το a~b βρίσκει ab· σε DSUM και τις άλλες database συναρτήσεις σκέτο κείμενο σημαίνει «αρχίζει από»· και το whole-cell Find πρέπει να κάνει backtracking μέσα στο τελευταίο *. Το HotXLS ακολουθεί αυτούς τους μετρημένους κανόνες από το v2.384.52, το v2.384.60 και το v2.384.64

Τα bug reports σε αυτή την περιοχή δεν αναφέρουν ποτέ wildcards. Λένε ότι μια report παραγόμενη από server μετράει μερικές σειρές λιγότερες από το ίδιο αρχείο ξαναϋπολογισμένο στην Excel, ή ότι κωδικός εξαρτήματος που περιέχει tilde βρίσκεται από έναν τύπο και αγνοείται από τον επόμενο. Η αιτία είναι ένας matcher που υποθέτει ότι ένα pattern σημαίνει το ίδιο παντού. Η Excel δεν δουλεύει έτσι, οπότε ούτε μπορεί μια engine της οποίας τα cached αποτελέσματα πρέπει να συμφωνούν με την Excel. Πριν το v2.384.52 το HotXLS περνούσε κάθε κριτήριο από DOS-style file mask, που έπιανε τα καθημερινά patterns σωστά και τα edge cases σιωπηλά λάθος

Γιατί ένα pattern string σημαίνει τέσσερα διαφορετικά πράγματα στην Excel;

Ένα pattern string σημαίνει τέσσερα διαφορετικά πράγματα επειδή η Excel κληρονόμησε τέσσερις κανόνες ταύτισης από τέσσερις δυνατότητες και δεν τους ενοποίησε ποτέ. Οι συναρτήσεις κριτηρίων (COUNTIF, SUMIF, AVERAGEIF και η οικογένεια *IFS) αποφασίζουν ανά κριτήριο αν θα ισχύσουν καθόλου wildcards. Οι lookup συναρτήσεις (MATCH με match type 0, XLOOKUP με match_mode 2) τις εφαρμόζουν πάντα. Οι database συναρτήσεις (DSUM, DCOUNTA και παρέα) ακολουθούν το Advanced Filter, όπου σκέτη λέξη είναι πρόθεμα. Το παράθυρο Find έχει δικά του whole-cell και partial modes. Ο πίνακας παρακάτω δείχνει ποια κελιά ταιριάζει κάθε pattern πάνω σε μία στήλη με a~b, ab, AB, abc, abcb, a*b και axb, με κάθε συνάρτηση στο προεπιλεγμένο case-insensitive mode της

PatternCOUNTIF / SUMIFMATCH(…,0) / XLOOKUP mode 2Κριτήριο DSUMFind, whole cell, wildcards on
abab, ABab, ABab, AB, abc, abcbab, AB
a*ba~b, ab, AB, abcb, a*b, axbίδιο με COUNTIFκάθε εγγραφή, abc συμπεριλαμβανομένουίδιο με COUNTIF
a~bμόνο a~bab, ABab, AB, abc, abcbab, AB
a~*bμόνο a*bμόνο a*bμόνο a*bμόνο a*b
=abab, ABδεν εφαρμόζεταιab, ABδεν εφαρμόζεται

Η σειρά a~b είναι εκεί όπου ο COUNTIF και ο MATCH διαφωνούν, και οι κωδικοί εξαρτημάτων και οι χειρόγραφοι κωδικοί περιέχουν tildes πιο συχνά απ’ όσο κανείς περιμένει. Η σειρά a*b δείχνει την άλλη παγίδα: το abc ταιριάζει για DSUM αλλά όχι για COUNTIF, επειδή η database συνάρτηση προσθέτει σιωπηλά ένα *. Οι εγγραφές DSUM για ab, a*b και =ab βγαίνουν κατευθείαν από τρεξίματα Excel 16· η εγγραφή DSUM για a~b ακολουθεί από τον ίδιο κανόνα προθέματος, αφού το προσαρτημένο * κάνει το κριτήριο wildcard pattern μέσα στο οποίο το ~b είναι escaped b

Πότε περνάει ο COUNTIF σε wildcard mode;

Ο COUNTIF περνάει σε wildcard mode μόνο όταν το κείμενο του κριτηρίου περιέχει * ή ?, escaped ή όχι. Χωρίς κανέναν από εκείνους τους χαρακτήρες, η Excel συγκρίνει το κριτήριο με κάθε κελί ως ολόκληρο string, χωρίς διάκριση πεζών, και μια tilde είναι απλώς tilde, οπότε ο COUNTIF(A1:A7,"a~b") μετράει το κελί που κυριολεκτικά κρατά a~b. Πρόσθεσε ένα σκέτο αστέρι και η σημασία αναποδογυρίζει: στο "a~b*" η tilde τώρα κάνει escape το b, το pattern διαβάζεται «ab ακολουθούμενο από οτιδήποτε», και το κελί a~b δεν μετράει πια. Το HotXLS εφαρμόζει αυτόν τον κανόνα και στις δύο engines από το v2.384.52, μέσω ενός criteria matcher στο lxCalc που μοιράζονται COUNTIF, SUMIF, AVERAGEIF, COUNTIFS, SUMIFS, AVERAGEIFS και οι database συναρτήσεις

Διάγραμμα πύλης wildcards HotXLS: ο COUNTIF και ο SUMIF εφαρμόζουν wildcards μόνο όταν το κριτήριο περιέχει αστέρι ή ερωτηματικό, οπότε το a~b μετράει το κυριολεκτικό κελί και δίνει 1, ενώ MATCH type 0 και XLOOKUP mode 2 είναι πάντα σε wildcard mode, οπότε το a~b βρίσκει το ab στη θέση 2
Η πύλη είναι όλη η διαφορά: ο COUNTIF ζητά αστέρι ή ερωτηματικό πριν μεταχειριστεί tilde ως escape, ο MATCH δεν ρωτά ποτέ, οπότε το ίδιο pattern string μετράει ένα κελί και βρίσκει το άλλο

Μέσα στο wildcard mode οι κανόνες escape είναι ίδιοι με παντού αλλού στην Excel: το ~ κάνει τον επόμενο χαρακτήρα κυριολεκτικό ό,τι κι αν είναι, οπότε ~b σημαίνει b και ~~ σημαίνει μία tilde, και tilde στο ακραίο τέλος του pattern πετιέται, οπότε το "a*~" συμπεριφέρεται ως "a*". Οι αγκύλες δεν είναι ποτέ ειδικές. Κριτήριο "[x]" μετράει κελιά που κρατούν τους τρεις χαρακτήρες [x], και το "[a-z]" δεν μετράει τίποτα σε συνηθισμένα δεδομένα. Το TXLSXWorkbook.Calculate αποτιμάει string τύπου πάνω στο active sheet και επιστρέφει Variant, ο γρηγορότερος τρόπος να τσεκάρετε αυτούς τους κανόνες πάνω στα δικά σας δεδομένα

uses
  System.Variants, lxHandleX;

const
  Names: array [1..7] of string = ('a~b', 'ab', 'AB', 'abc', 'abcb', 'a*b', 'axb');
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  i: Integer;

  procedure Show(const Formula: string);
  begin
    Writeln(Formula, ' = ', VarToStr(Book.Calculate(Formula)));
  end;

begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Data');
    for i := 1 to High(Names) do
    begin
      Sheet.Cells[i, 1].Value := Names[i];
      Sheet.Cells[i, 2].Value := 1 shl (i - 1);  // 1, 2, 4 ... ώστε ένα άθροισμα SUMIF να δείχνει τις σειρές του
    end;
    Sheet.Cells[8, 1].Value := 5;                // αριθμός· το A9 μένει κενό

    Show('=COUNTIF(A1:A7,"a~b")');      // 1    χωρίς * ή ?: σκέτο κείμενο, το κελί a~b
    Show('=COUNTIF(A1:A7,"a~b*")');     // 4    wildcard mode: ab, AB, abc, abcb
    Show('=COUNTIF(A1:A7,"a*b")');      // 6    wildcard ολόκληρου string, το abc αποκλείεται
    Show('=SUMIF(A1:A7,"a*b",B1:B7)');  // 119  κάθε σειρά εκτός abc (8)
    Show('=COUNTIF(A1:A7,"a~*b")');     // 1    το κυριολεκτικό a*b
    Show('=COUNTIF(A1:A9,"<>ab")');     // 7    ο αριθμός 5 και το κενό A9 μετρούν
    Show('=COUNTIF(A1:A9,"<>")');       // 8    μη κενά κελιά
  finally
    Book.Free;
  end;
end.

Τι μετράει το «<>text»;

Ένα κριτήριο "<>text" μετράει κάθε κελί που δεν είναι εκείνο το κείμενο, και στην Excel 16 αυτό περιλαμβάνει αριθμούς, booleans, τιμές σφάλματος και κενά κελιά. Ένα σκέτο "<>" είναι εντελώς άλλη ερώτηση: σημαίνει «όχι κενό κελί», οπότε προσπερνά άδεια κελιά αλλά μετράει κάθε τιμή, συμπεριλαμβανομένου του κενού κειμένου που επιστρέφει τύπος όπως ="". Ο παλιός κώδικας HotXLS έπιανε τα κελιά κειμένου σωστά αλλά όχι τους αριθμούς: μια ανισότητα Variant έκανε το Delphi να μετατρέψει το 'ab' σε αριθμό, η μετατροπή πετούσε exception, ένας handler το κατάπιε ως «καμία αντιστοιχία», και τα αριθμητικά κελιά έπεφταν σιωπηλά από το μέτρημα. Η πλευρά των κενών κελιών σε αυτή την ιστορία, συμπεριλαμβανομένου του τι ισούται ένας κενός τελεστέος σε συνηθισμένη σύγκριση, καλύπτεται στο πώς χειρίζεται το HotXLS comparison chains, κενά κελιά και SUMIF

Γιατί ο MATCH βρίσκει ab όταν ψάχνετε a~b;

Ο MATCH βρίσκει ab όταν ψάχνετε a~b επειδή ο MATCH με match type 0 και ο XLOOKUP με match_mode 2 είναι πάντα σε wildcard mode, οπότε η tilde είναι escape ακόμα κι όταν το pattern δεν περιέχει * ή ?. Η Excel 16 το επιβεβαιώνει σε range δύο κελιών με a~b και ab: ο MATCH("a~b",D1:D2,0) επιστρέφει 2, και σε range που κρατά μόνο a~b η ίδια κλήση επιστρέφει #N/A. Για να ψάξετε το κυριολεκτικό κείμενο a~b πρέπει να γράψετε "a~~b". Εν τω μεταξύ ο COUNTIF(D1:D2,"a~b") πάνω στα ίδια δύο κελιά επιστρέφει 1, μετρώντας το άλλο κελί. Ίδιο string, ίδιο range, αντίθετο κελί

Γι’ αυτό το HotXLS κρατά τις δύο αποφάσεις χώρια αντί πίσω από ένα «ταίριαξε pattern» σημείο εισόδου. Ο matcher καθαυτός είναι μοιρασμένος: από το v2.384.52, MATCH, XLOOKUP και οι συναρτήσεις κριτηρίων τρέχουν τον ίδιο backtracking matcher, με τον ίδιο χειρισμό escape και τον ίδιο κανόνα trailing-tilde. Αυτό που διαφέρει είναι η πύλη μπροστά του. Η διαδρομή κριτηρίων ρωτά πρώτα «περιέχει αυτό το κείμενο * ή ?;»· η διαδρομή lookup δεν ρωτά ποτέ. Η ένωσή τους θα διόρθωνε τη μία οικογένεια και θα έσπαγε την άλλη, και οι δύο κατευθύνσεις τσεκάρονται με τιμές Excel 16 και στις δύο engines. Τα wildcard lookups έχουν επίσης δική τους προϋπόθεση: ο XLOOKUP απορρίπτει wildcard matching συνδυασμένο με binary search mode, κανόνας που περιγράφεται στον οδηγό HotXLS για search modes των XLOOKUP και XMATCH

Πώς διαβάζουν το DSUM και οι database συναρτήσεις ένα σκέτο κειμενικό κριτήριο;

Το DSUM και οι άλλες database συναρτήσεις διαβάζουν κειμενικό κριτήριο χωρίς αρχικό =, < ή > ως «αρχίζει από», με wildcards ακόμα ενεργά. Είναι ο κανόνας του Advanced Filter, και διαφέρει από τον COUNTIF σκόπιμα. Η Excel 16 μετρήθηκε πάνω από στήλη Name με abc, ab, xab, AB, a~b και a*b: το κριτήριο ab ταιριάζει abc, ab και AB· το =ab ταιριάζει μόνο ab και AB· το <>ab είναι ανισότητα ολόκληρης εγγραφής· τα a*b και a? είναι κι αυτά patterns προθέματος· το >ab είναι συνηθισμένη σύγκριση. Πριν το v2.384.64 το HotXLS ταίριαζε το ab ακριβώς, οπότε ένα DSUM πάνω σε εκείνα τα test δεδομένα επέστρεφε 10 εκεί που η Excel επιστρέφει 11

Η διόρθωση έπρεπε να παρακάμψει τον condition parser, που διπλώνει και το ab και το =ab στην ίδια συνθήκη ισότητας. Το HotXLS επομένως επιθεωρεί το ακατέργαστο κείμενο του κριτηρίου πριν εμπιστευτεί την parsed συνθήκη: κειμενικό κριτήριο του οποίου ο πρώτος χαρακτήρας δεν είναι =, < ή > παίρνει προσαρτημένο * και περνά από τον wildcard matcher, και όλα τα άλλα κρατούν τη σύγκριση ολόκληρης εγγραφής. Μια πρακτική σημείωση όταν χτίζετε criteria ranges σε κώδικα: στην XLSX engine η ανάθεση του string '=ab' στο TXLSXCell.Value αποθηκεύει κείμενο, ενώ η classic engine TXLSWorkbook μεταγλωττίζει τιμή που αρχίζει από = ως τύπο εκτός αν την προμηθεύσετε με απόστροφο

Διάγραμμα HotXLS του κανόνα κριτηρίου DSUM: σκέτο κειμενικό κριτήριο παίρνει προσαρτημένο αστέρι και ταιριάζει ως πρόθεμα ώστε το ab φτάνει ab, AB, abc και abcb, το equals ab συγκρίνει ολόκληρη την εγγραφή, το angle bracket ab αποκλείει και τα δύο, και tilde αστέρι επιζεί ως κυριολεκτικό a*b, με τα μετρημένα σύνολα DSUM 30, 6, 121 και 32
Η Excel κληρονόμησε τον κανόνα Advanced Filter για τις database συναρτήσεις: σκέτο κείμενο σημαίνει αρχίζει από, ενώ αρχικό equals ή not-equals συγκρίνει ολόκληρη την εγγραφή· το HotXLS επιθεωρεί το ακατέργαστο κείμενο του κριτηρίου πριν εμπιστευτεί την parsed συνθήκη
const
  Names: array [1..7] of string = ('a~b', 'ab', 'AB', 'abc', 'abcb', 'a*b', 'axb');
  Criteria: array [0..4] of string = ('ab', '=ab', '<>ab', 'a*b', 'a~*');
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  i: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Db');
    Sheet.Cells[1, 1].Value := 'Name';
    Sheet.Cells[1, 2].Value := 'Val';
    for i := 1 to High(Names) do
    begin
      Sheet.Cells[i + 1, 1].Value := Names[i];
      Sheet.Cells[i + 1, 2].Value := 1 shl (i - 1);
    end;
    Sheet.Cells[1, 4].Value := 'Name';            // header κριτηρίων στο D1
    for i := 0 to High(Criteria) do
    begin
      Sheet.Cells[2, 4].Value := Criteria[i];     // μένει κείμενο στην XLSX engine
      Writeln(Criteria[i], ' -> ',
        VarToStr(Book.Calculate('=DSUM(A1:B8,"Val",D1:D2)')));
    end;
    // ab   -> 30   ab, AB, abc, abcb (αρχίζει από)
    // =ab  -> 6    ab, AB (ολόκληρη εγγραφή)
    // <>ab -> 121  όλα εκτός ab και AB
    // a*b  -> 127  το a*b* ταιριάζει όλα τα επτά, abc συμπεριλαμβανομένου
    // a~*  -> 32   μόνο το κυριολεκτικό a*b
  finally
    Book.Free;
  end;
end;

Μία συγγενής διαφορά επέζησε της διόρθωσης προθέματος και μετράει σε παλαιότερα builds. Οι συγκρίσεις κειμένου όπως >ab χρησιμοποιούσαν σειρά code points, ενώ η Excel βάζει τη στίξη πριν από τα γράμματα, οπότε ο "a~b">"ab" είναι FALSE στην Excel και ήταν TRUE στο HotXLS. Από το v2.384.67 τα κριτήρια > και <, μαζί με τη συνηθισμένη σύγκριση κειμένου και την ταξινόμηση, χρησιμοποιούν το word-sort collation της Excel κάτω από το τρέχον user locale, και τα δύο ξανασυμφωνούν

Γιατί το whole-cell Find έχασε το abcb;

Το whole-cell Find έχασε το abcb επειδή ο matcher σταμάταγε στο πρώτο σημείο όπου το pattern εξαντλήθηκε αντί να κάνει backtracking μέσα στο τελευταίο *. Ο matcher μερικής αντιστοιχίας πίσω από το Replace επιστρέφει μόλις εξαντληθεί το pattern· το whole-cell Find τον ξαναχρησιμοποιούσε και μετά απαιτούσε η αντιστοιχία να καλύψει όλο το κελί: το a*b πάνω στο abcb σταμάταγε μετά το ab, είχε καταναλώσει 2 χαρακτήρες από 4, και απορριπτόταν. Από το v2.384.60 ο whole-cell matcher είναι ξεχωριστή υλοποίηση που μεταχειρίζεται «τέλος pattern, όχι τέλος κειμένου» ως ακόμα μία ασυμφωνία και ξαναδοκιμάζει από το τελευταίο αστέρι, οπότε το a*b ταιριάζει abcb και το a?b*b ταιριάζει axbyb, όπως κάνει το Excel 16 Find με τσεκαρισμένο το «Match entire cell contents»

Διάγραμμα HotXLS του backtracking του whole-cell wildcard Find: το pattern a*b καταναλώνει a και b στο κελί abcb και ο παλιός matcher σταμάτησε με το pattern εξαντλημένο και απέρριψε το κελί, ενώ ο τρέχων matcher μεταχειρίζεται pattern εξαντλημένο με κείμενο που απομένει ως ακόμα μία ασυμφωνία και ξαναδοκιμάζει από το τελευταίο αστέρι έως ότου όλο το κελί ταιριάξει
Μια whole-cell αντιστοιχία δεν έχει τελειώσει όταν το pattern εξαντλείται· το να μεταχειριστείς το υπόλοιπο κείμενο ως ακόμα μία ασυμφωνία στέλνει τον matcher πίσω στο τελευταίο αστέρι, που είναι πώς το a*b φτάνει το abcb όπως το Excel 16 Find

Το ίδιο release άλλαξε και την tilde. Το Excel 16 Find, και σε whole-cell και σε partial mode, μεταχειρίζεται το ~ ως escape για οποιονδήποτε επόμενο χαρακτήρα: το a~b βρίσκει ab, το a~~b βρίσκει a~b, και ακραία tilde αγνοείται, οπότε το q~ συμπεριφέρεται ως q. Ο παλαιότερος matcher του HotXLS αναγνώριζε μόνο ~*, ~? και ~~ ως escapes, οπότε το a~b έβρισκε το κείμενο a~b. Find pattern με σκέτο ~ είναι ασταθές στην ίδια την Excel, ταιριάζει οποιοδήποτε κελί σαν κενό pattern, και το HotXLS δεν μιμείται αυτό

Στην XLSX engine η αναζήτηση είναι η TXLSXWorksheet.FindText με ένα σύνολο TXLSXFindOptions: το lxfUseWildcards ανάβει *, ? και ~, το lxfWholeCell απαιτεί να ταιριάξει όλο το κελί, και το lxfMatchCase κάνει τη σύγκριση ευαίσθητη σε πεζά-κεφαλαία. Χωρίς lxfUseWildcards κάθε χαρακτήρας, αστέρι συμπεριλαμβανομένου, είναι κυριολεκτικός. Το Find κοιτάζει μόνο τιμές κειμένου· τα αριθμητικά κελιά προσπερνιούνται, και τα κελιά τύπων προσπερνιούνται εκτός αν οριστεί lxfSearchFormulas, οπότε ψάχνεται το κείμενο του τύπου. Η άγκυρα που δίνουν StartRow και StartCol είναι inclusive, οπότε ένας βρόχος Find All προχωρά μία στήλη πέρα από κάθε χτύπημα

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Row, Col, NextRow, NextCol, Changed: Integer;
  Opts: TXLSXFindOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Parts');
    Sheet.Cells[1, 1].Value := WideString('abc');
    Sheet.Cells[2, 1].Value := WideString('abcb');
    Sheet.Cells[3, 1].Value := WideString('a~b');
    Sheet.Cells[4, 1].Value := WideString('ab');

    Opts := [lxfUseWildcards, lxfWholeCell];
    if Sheet.FindText('a*b', Row, Col, Opts, 1, 1) then
      Writeln('a*b  whole cell -> row ', Row);   // 2: το abc απορρίφθηκε, το abcb κάνει backtrack
    if Sheet.FindText('a~b', Row, Col, Opts, 1, 1) then
      Writeln('a~b  whole cell -> row ', Row);   // 4: το ~b είναι escaped b
    if Sheet.FindText('a~~b', Row, Col, Opts, 1, 1) then
      Writeln('a~~b whole cell -> row ', Row);   // 3: το ~~ είναι μία κυριολεκτική tilde

    // Μερική αντιστοιχία, Find All: το κελί άγκυρας περιλαμβάνεται, οπότε προχώρα πέρα από κάθε χτύπημα
    NextRow := 1;
    NextCol := 1;
    while Sheet.FindText('a*b', Row, Col, [lxfUseWildcards], NextRow, NextCol) do
    begin
      Writeln('a*b  contained in row ', Row);     // σειρές 1, 2, 3 και 4
      NextRow := Row;
      NextCol := Col + 1;
    end;

    // Whole-cell wildcard replace ξαναγράφει μόνο το κυριολεκτικό a~b
    Changed := Sheet.ReplaceText('a~~b', 'a-b', Opts);
    Writeln(Changed, ' cell(s) replaced');         // 1
  finally
    Book.Free;
  end;
end;

Ο μερικός βρόχος βρίσκει και τις τέσσερις σειρές, συμπεριλαμβανομένου του abc, επειδή σε partial mode το a*b χρειάζεται μόνο να εμφανιστεί κάπου μέσα στο κελί. Τα FindTextIn και ReplaceTextIn παίρνουν τις ίδιες επιλογές συν ένα παράθυρο FirstRow, FirstCol, LastRow, LastCol, το προγραμματιστικό ισοδύναμο της αναζήτησης μέσα σε επιλογή. Η classic engine εκθέτει τους ίδιους κανόνες μέσω overload με τρία booleans, TXLSWorksheet.FindText(SearchText, Row, Col, MatchCase, UseWildcards, WholeCell), συν matching overload του ReplaceText, με αποτελέσματα σειράς και στήλης με βάση το 1:

var
  Classic: IXLSWorkbook;
  Sheet: TXLSWorksheet;
  Row, Col: Integer;
begin
  Classic := TXLSWorkbook.Create;
  Sheet := Classic.Sheets.Add;
  Sheet.Range['A1', 'A1'].Value := 'abcb';
  // MatchCase = False, UseWildcards = True, WholeCell = True
  if Sheet.FindText('a*b', Row, Col, False, True, True) then
    Writeln('found at ', Row, ',', Col);           // 1,1
  if not Sheet.FindText('a*c', Row, Col, False, True, True) then
    Writeln('a*c does not cover abcb');
end;

Τι έκανε λάθος ο παλιός matcher DOS-mask;

Ο παλιός matcher έκανε λάθος τους ειδικούς χαρακτήρες, επειδή μια DOS file mask είναι άλλη γλώσσα από wildcard της Excel. Πριν το v2.384.52 οι συναρτήσεις κριτηρίων και οι database συναρτήσεις περνούσαν κάθε pattern στο MatchesMask, έναν file-mask matcher στη μονάδα lxMasks. Η σύνταξή του επικαλύπτεται με της Excel για τα συνηθισμένα cases, γι’ αυτό το πρόβλημα έμεινε κρυμμένο, αλλά αποκλίνει εκεί που τα πραγματικά δεδομένα γίνονται ενδιαφέροντα:

  • Το [x] διαβαζόταν ως σύνολο χαρακτήρων, οπότε ο COUNTIF(A1:A10,"[x]") μετρούσε κελιά που κρατούν x αντί για το κείμενο με αγκύλες, και το "[a-z]" ταιρίαζε οποιοδήποτε κελί ενός γράμματος
  • Δεν υπήρχε tilde escape, οπότε το "a~*b" δεν μπορούσε να ταιριάξει κυριολεκτικό asterisk
  • Λάθος mask, όπως ανοιχτή αγκύλη, πετούσε exception που ο καλών το κατάπιε ως «καμία αντιστοιχία», μετατρέποντας typo σε κριτήριο σε σιωπηλά λάθος σύνολο
  • Στην πλευρά lookup, το MATCH και το XLOOKUP μεταχειρίζονταν μόνο ~*, ~? και ~~ ως escapes, οπότε ο MATCH("a~b",…,0) έβρισκε το κυριολεκτικό a~b αντί για ab

Αν τα workbooks σας χρησιμοποίησαν ποτέ μόνο * και ? πάνω σε σκέτα alphanumeric δεδομένα, τα αποτελέσματα ήταν ήδη σωστά και δεν θα αλλάξουν. Αν περιέχουν αγκύλες, tildes, στήλες μικτών τύπων κάτω από "<>text", ή DSUM κριτήρια γραμμένα ως σκέτες λέξεις, ο επανυπολογισμός τους με v2.384.64 ή νεότερο μπορεί να αλλάξει σύνολα, και τα νέα σύνολα είναι όσα δείχνει η Excel. Η ίδια διάκριση ανάμεσα στο πώς αποθηκεύει η Excel ένα κριτήριο και πώς το συγκρίνει βγαίνει και για αποθηκευμένα filters, που συζητιούνται στο άρθρο HotXLS για BIFF8 AutoFilter DOPER κριτήρια

Σύντομη αναφορά: κανόνες wildcards της Excel στο HotXLS

  • COUNTIF, SUMIF, AVERAGEIF και η οικογένεια *IFS χρησιμοποιούν wildcards μόνο όταν το κριτήριο περιέχει * ή ?· αλλιώς συγκρίνουν ολόκληρα strings χωρίς διάκριση πεζών και το ~ είναι κυριολεκτικό (από το v2.384.52)
  • MATCH με match type 0 και XLOOKUP με match_mode 2 χρησιμοποιούν πάντα wildcards, οπότε το a~b βρίσκει ab και το κυριολεκτικό θέλει a~~b (από το v2.384.52)
  • Σε wildcard mode το ~ κάνει escape οποιονδήποτε επόμενο χαρακτήρα και ακραίο ~ πετιέται· τα [ και ] είναι συνηθισμένοι χαρακτήρες
  • Το "<>text" μετράει αριθμούς, booleans, σφάλματα και κενά κελιά· ένα σκέτο "<>" μετράει μη κενά κελιά, αποτελέσματα ="" συμπεριλαμβανομένων
  • Το DSUM και οι άλλες database συναρτήσεις μεταχειρίζονται σκέτο κείμενο ως «αρχίζει από»· τα =text και <>text συγκρίνουν ολόκληρη την εγγραφή (από το v2.384.64)
  • Το whole-cell Find με lxfUseWildcards και lxfWholeCell κάνει backtracking, οπότε το a*b ταιριάζει abcb· Find και Replace μεταχειρίζονται το ~ ως escape για οποιονδήποτε χαρακτήρα (από το v2.384.60)
  • Η σειρά κειμένου στα κριτήρια > και < ακολουθεί το word-sort collation της Excel, στίξη πριν από γράμματα (από το v2.384.67)

Η συμβατότητα Excel σε formula engine είναι κυρίως edge cases σαν αυτά, μετρημένα πάνω στην Excel και όχι μαντεμένα από τεκμηρίωση. Το HotXLS αποτιμάει COUNTIF, MATCH, XLOOKUP, DSUM και την υπόλοιπη βιβλιοθήκη συναρτήσεων του εγγενώς σε Delphi και C++Builder, και στις δύο engines, classic και XLSX, χωρίς εγκατεστημένη Excel. Λεπτομέρειες, εκδόσεις και δοκιμαστική λήψη στη σελίδα HotXLS Delphi spreadsheet component