Pytanie „czy ten skoroszyt nadal definiuje nazwę TaxRate?" odpowiada mały kawałek pakietu XLSX. Nazwy arkuszy, ich stany ukryte albo bardzo ukryte i wszystkie nazwy zdefiniowane na poziomie skoroszytu mieszkają w xl/workbook.xml (ECMA-376 Part 1, §18.2), zwykle w kilku kilobajtach. Komórki mieszkają gdzie indziej — jedna część xl/worksheets/sheetN.xml na arkusz plus tabela współdzielonych łańcuchów — i tam jest prawie cała masa bajtów. Loader upierający się przy zbudowaniu kompletnego skoroszytu, zanim wolno cokolwiek zapytać, rozepchnie i sparsuje wszystko, dla każdego pliku w folderze, by odpowiedzieć na pytanie, które pierwsze kilka kilobajtów już rozstrzygnęło
Podział, który się liczy, biegnie między czytaniem kształtu skoroszytu a czytaniem jego ciężaru. Loader potrafiący stanąć po kształcie, wczytać ciężar dla wybranego podzbioru arkuszy i dociągnąć jeszcze jeden arkusz później zamienia „otwórz skoroszyt" z kroku wszystko albo nic w coś, co określasz per wywołanie. Przynosi też dwie pułapki — jedną o podstawach indeksowania i drugą o tym, jak długo plik źródłowy musi leżeć spokojnie
Co czyta Open tylko-metadane i gdzie się zatrzymuje
Przy TXLSXWorkbook.MetadataOnly ustawionym na True przed Open HotXLS parsuje część skoroszytu, style, motyw, nazwy zdefiniowane, nazwy arkuszy i stany widoczności, połączenia, linki zewnętrzne oraz właściwości dokumentu z docProps/core.xml i docProps/app.xml. Potem pomija XML komórek każdego arkusza i satelickie części per arkusz: komentarze, rysunki i tabele. Tabela współdzielonych łańcuchów też jest pomijana — co znaczy więcej, niż brzmi, bo w skoroszytach pełnych tekstu ta jedna część potrafi dorównać rozmiarem arkuszom
Skoroszyt, który wraca, jest strukturalnie kompletny. Sheets.Count jest prawidłowy, każdy arkusz ma swoją prawdziwą nazwę i Visibility, DefinedNames jest zaludniony, ExternalLinks wypisuje każdy cel linku, a Title, Author i LastModifiedBy niosą to, co plik zadeklarował. Każdy arkusz ma po prostu zero komórek
To dokładnie dane, na których biegnie audyt. Które skoroszyty ukrywają arkusz? Które nadal definiują TaxRate — i co? Które linkują do udziału plikowego wyłączonego w zeszłym roku? Skaner poniżej odpowiada na wszystkie trzy dla jednego pliku. Zauważ dwie konwencje indeksowania obok siebie: Sheets[] jest liczone od 1, w rytm silnika XLS, podczas gdy DefinedNames i ExternalLinks to zwykłe listy liczone od 0
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // czytane przez Open, więc ustaw najpierw
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] liczone od 1
case Workbook.Sheets[I].Visibility of
xlsxSheetHidden:
Report.Add(FileName + ': hidden sheet ' + Workbook.Sheets[I].Name);
xlsxSheetVeryHidden:
Report.Add(FileName + ': very hidden sheet ' + Workbook.Sheets[I].Name);
end;
Name := Workbook.DefinedNames.FindByName('TaxRate');
if Name <> nil then
Report.Add(FileName + ': TaxRate = ' + Name.Formula);
for I := 0 to Workbook.ExternalLinks.Count - 1 do // lista liczona od 0
Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
finally
Workbook.Free;
end;
end;
Skaner czyta Visibility zamiast Booleana Visible celowo. Visible jest False dla obu stanów ukrycia, a arkusz bardzo ukryty to ten, którego audyt najbardziej chce flagować — bo okno dialogowe Excela Odkryj go nie wypisuje i tylko VBA albo bezpośrednia edycja pliku przyprowadza go z powrotem
Wczytywanie dwóch arkuszy z czterdziestu przez SelectedSheets
SelectedSheets to TStringList należąca do skoroszytu. Gdy trzyma co najmniej jedną nazwę, Open parsuje XML komórek wyłącznie dla arkuszy, które wypisuje; każdy inny arkusz jest tworzony ze swoją poprawną nazwą i widocznością i bez komórek. Pusta lista, stan domyślny, wczytuje wszystkie arkusze. Nazwy są szukane przez własne IndexOf listy, więc przy domyślnych ustawieniach dopasowanie ignoruje wielkość liter — co zgadza się z regułą Excela, że dwie nazwy arkuszy w jednym skoroszycie nie mogą różnić się wyłącznie wielkością liter
Workbook := TXLSXWorkbook.Create;
Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
if Workbook.Open('consolidated-2026.xlsx') = 1 then
ShowSheet(Workbook.Sheets[1]); // ShowSheet to twój własny kod UI
Te dwa przełączniki współgrają tylko w jedną stronę. MetadataOnly = True nadpisuje wybór i pomija wszystkie arkusze, więc skoroszyt skonfigurowany z oboma wraca bez komórek gdziekolwiek, a wybór wygląda na zepsuty, choć został po prostu zdystansowany. Używaj jednego na Open: tylko-metadane do odkrywania, wybór do pracy celowanej
Ładowanie selektywne biegnie też przez fazę parsowania równoległego, gdy ParallelParse jest włączone; niewybrane arkusze są pomijane wewnątrz zadań wątków, więc wątki spędzają czas na arkuszach, o które prosiłeś. Te same dwie właściwości obowiązują, gdy źródłem jest binarny skoroszyt .xlsb, z jedną regułą zakresu: .xlsb niosący projekt VBA musi być otwierany bez tych dwóch opcji; przy którejkolwiek ustawionej Open raportuje odczyt selektywny jako niewspierany
Dlaczego LoadSheet(1) wczytuje zły arkusz?
Bo LoadSheet bierze pozycję liczoną od 0, podczas gdy Sheets[] jest liczone od 1. LoadSheet(1) materializuje drugi arkusz skoroszytu, a ponieważ wczytanie arkusza, który już ma komórki, jest no-opem zwracającym True, pomyliowany indeks może wyglądać na sukces, podczas gdy arkusz, w który kliknął użytkownik, zostaje pusty. Konwersja należy w dokładnie jedno miejsce w twoim kodzie
Druga pułapka to żywotność pliku. LoadSheet nie trzyma archiwum otwartego między wywołaniami. Otwiera ponownie oryginalny plik po nazwie, parsuje część skoroszytu, style i współdzielone łańcuchy, po czym parsuje żądany arkusz. Zanim zaufa temu plikowi, HotXLS porównuje jego rozmiar i czas ostatniego zapisu z wartościami przechwyconymi przy Open; jeśli inny proces zapisał skoroszyt po drodze, LoadSheet zwraca False zamiast mieszać komórki z innej wersji do modelu, który już trzymasz
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo liczone od 1, jak Sheets[]
var
Sheet: TXLSXWorksheet;
begin
Sheet := FWorkbook.Sheets[SheetNo];
if Sheet.Cells.Count = 0 then
if not FWorkbook.LoadSheet(SheetNo - 1) then // LoadSheet chce pozycji liczonej od 0
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // twój własny kod siatki
end;
Z tego projektu płynie kilka konsekwencji. Skoroszyt otwarty z TStream nie ma pliku źródłowego, do którego można wrócić, więc LoadSheet zwraca False dla .xlsx otwartego ze strumienia; to samo tyczy skoroszytu zaszyfrowanego, który jest odszyfrowywany do pamięci przed parsowaniem. Binarny .xlsb otwierany selektywnie ze strumienia jest wyjątkiem, bo HotXLS odkłada tymczasową kopię strumienia dla odłożonych arkuszy. A arkusz naprawdę pusty będzie czytany od nowa przy każdym wywołaniu, bo zero komórek to też to, jak wygląda arkusz pominięty
Model kosztów rozstrzyga, jak go używać. Każde wywołanie LoadSheet parsuje ponownie tabelę współdzielonych łańcuchów, więc materializowanie dziesięciu arkuszy po kolei płaci za tę część dziesięć razy. Dla przeglądarki, w której użytkownik otwiera jeszcze jedną zakładkę od czasu do czasu, to uczciwa cena. Gdy znasz już zestaw arkuszy, otwórz ponownie z SelectedSheets: jeden przebieg archiwum, jedno parsowanie współdzielonych łańcuchów. Jest i różnica w treści: LoadSheet przynosi komórki, wartości, formuły i formaty arkusza, podczas gdy komentarze, rysunki i tabele przychodzą z Open — więc arkusze, których komentarze albo tabele czyta twój kod, należą do wyboru z góry
Dlaczego podróż w obie strony zmienia części, których nigdy nie tknąłeś?
Zapis, który regeneruje wszystko, przepisuje bajty, które nigdy się nie zmieniły. Rozepchnij motyw, zbuduj model obiektowy, zserializuj go, skompresuj jeszcze raz — a wynik różni się od oryginału, choć nikt nie ruszył motywu. Dla projektów VBA i cache tabel przestawnych koszt jest wyższy niż hałaśliwy diff: projekt VBA to binarny plik złożony z własnymi wewnętrznymi strumieniami, a cache tabeli przestawnej trzyma migawkę danych źródłowych, których pola definicje tabeli przestawnej adresują po pozycji — więc najbezpieczniejsze, co można zrobić z każdym z nich, gdy nikt go nie zmienił, to zostawić jego bajty w spokoju
PreserveRawParts, domyślnie True, każe SaveAs kopiować skompresowane bajty niezmienionych części prosto z archiwum źródłowego. To obejmuje motyw, projekt VBA, połączenia, wykresy, osadzone media, ustawienia drukarki, tabele przestawne oraz definicje i rekordy cache tabel przestawnych — każde tylko dopóki model mówi, że nie było modyfikowane. Te części nigdy nie są rozepchnywane ani ponownie upychane po drodze. Ustawienie właściwości na False wymusza pełną reserializację każdej części — to przełącznik, po który sięgasz, gdy chcesz, by wyjście odzwierciedlało wyłącznie to, co zawiera model obiektowy
Ścieżka surowej kopii używa tej samej kontroli pliku co LoadSheet: źródło musi nadal istnieć z rozmiarem i znacznikiem czasu zobaczonym przy Open. Gdy nie istnieje, SaveAs serializuje te części normalnie i plik nadal jest kompletny. Dwa przypadki zawsze idą ścieżką serializacji: skoroszyt, którego źródłem był binarny .xlsb, konwertowany na XML przy odczycie, oraz zapis z ustawionym StrictOOXML, gdzie przestrzenie nazw części się zmieniają
Zapis częściowo wczytanego skoroszytu wymaga decyzji. Dla źródła .xlsb SaveAs odmawia, dopóki jakikolwiek arkusz jest jeszcze odłożony, i zapisuje „Load every deferred binary worksheet before saving" w Diagnostics. Dla .xlsx traktuj selektywnie wczytany skoroszyt jako widok do czytania: arkusz kikut nie ma komórek do wyemitowania przez zapisującego, więc albo zawołaj LoadSheet dla każdego kikuta najpierw, albo otwórz ponownie bez wyboru, zanim edytujesz i zapiszesz
Potok wykrywania, który otwiera ponownie celowo
Wzorzec, który się broni, ma dwa przebiegi. Przebieg pierwszy otwiera każdy plik tylko-metadane i rozstrzyga. Przebieg drugi otwiera ponownie wyłącznie pliki, które pasowały, z wyborem arkuszy, i robi robotę. Ponowne otwieranie wygląda na marnotrawstwo, dopóki nie policzysz, czego uniknął przebieg pierwszy: drugie Open jest jedynym, które parsuje komórki — i parsuje tylko arkusze wybrane w przebiegu pierwszym
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Przebieg 1: odkrywanie, bez komórek
Probe := TXLSXWorkbook.Create;
try
Probe.MetadataOnly := True;
if Probe.Open(FileName) <> 1 then
Exit;
for I := 1 to Probe.Sheets.Count do
if Pos('Invoice', Probe.Sheets[I].Name) > 0 then
Wanted.Add(Probe.Sheets[I].Name);
finally
Probe.Free;
end;
if Wanted.Count = 0 then
Exit; // nic w tym pliku nie jest wart parsowania
// Przebieg 2: komórki tylko dla wybranych arkuszy
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // twój własny kod przetwarzania
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
Zerwanie MetadataOnly po Open nie zamieni sondy w pełny skoroszyt. Flaga jest czytana podczas otwierania; zmiana jej później dotyczy tylko następnego Open na tym obiekcie. Sonda może nadal dociągać arkusz po arkuszu przez LoadSheet i dla jednego czy dwóch arkuszy to rozsądne, ale potok lepiej obsłuży czyste ponowne otwarcie, które przy okazji przynosi komentarze i tabele każdego arkusza
Gdzie to się mieści z innymi dźwigniami dużego pliku
Ładowanie selektywne tnie to, co parsujesz. Nie robi niczego dla kosztu arkusza, gdy już jest wczytany, i nie pomoże, gdy zadanie potrzebuje każdej komórki każdego arkusza. Ten przypadek jest o magazynie — pokrywa go artykuł pamięć komórek w blokach wierszy i style oparte na zakresach — i o przepustowości parsowania, pokrytej w artykule równoległe parsowanie XLSX i alokator pamięci. Gdy zadaniem jest czysty odczyt bez żadnej edycji, strumieniowy odczyt bezpośredni omija model obiektowy skoroszytu całkowicie i jest lepszym narzędziem niż jakakolwiek strategia ładowania, która jeden buduje
Całość biegnie wewnątrz twojego własnego pliku wykonywalnego Delphi albo C++Builder, bez instalacji Excela na serwerze, który skanuje. Wspierane formaty, edycje i licencjonowanie są wypisane na stronie komponentu arkuszowego HotXLS Delphi