HotXLS מאחסן תאי גיליון עבודה בבלוקים קומפקטיים של 256 שורות, פותר עיצוב שורה, עמודה ומלבן דרך שכבות interval עצלות במקום ליצור אובייקטי תא, וזורם כל שורה ישירות לתוך זרם ה-deflate של החבילה בשמירה. יחד שלושת השינויים הללו מכריעים את פרופיל הזיכרון של חוברת עבודה גדולה: שיא השימוש עוקב אחר השורה הבודדת הגדולה ביותר ולא אחר גודל ה-XML השלם של גיליון העבודה
הסיבה שזה חשוב היא צורה שכל מפתח גיליון אלקטרוני פוגש בסופו של דבר. משתמש מעצב עמודה שלמה — קליק אחד, מיליון תאים — ומודל אובייקט נאיבי עונה על ידי הקצאת מיליון אובייקטי תא להחזיק אינדקס פורמט-מספר אחד. הקובץ על הדיסק נשאר זעיר מפני שפורמט XLSX מביע זאת כרשומת <col> יחידה. התהליך אינו נשאר זעיר כלל
למה עיצוב עמודה עולה יותר זיכרון מאשר מילויה?
מפני שלעיצוב אין נתונים שיצדיקו את האובייקט. תא עם ערך חייב להתקיים איפשהו. תא שריק אך מסוגנן קיים רק כדי לשאת אינדקס סגנון, ומימוש מיליונים כאלה הוא הדרך הקלאסית שבה יישום גיליון אלקטרוני בדלפי נגמר לו מרחב הכתובות על קובץ ש-Excel פותח מיידית
שכבות סגנון interval מסירות את הצורך. הוראת עיצוב שורה, עמודה או מלבן מאוחסנת פעם אחת כטווח בתוספת חלקי הסגנון שהיא תורמת, ונפתרת בעצלתניים כשתא באותו טווח מונגש בפועל. שכבות שורדות עריכות מבניות — הוספת שורה בתוך בלוק מעוצב מזיזה את ה-interval במקום לבנותו מחדש — והן עוברות מעבר-הלוך כרשומות עמודה, שורה ותא-סגנון-בלבד קומפקטיות, שזה בדיוק איך Excel כותב אותן
var
Sheet: TXLSXWorksheet;
State: TXLSXCellStyleState;
begin
Sheet := Workbook.Sheets[1];
// Style indexes come from the workbook style pools, e.g. from a cell
// you have already formatted the way you want the range to look
State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
// Format columns B..D without creating a single empty cell object
Sheet.StyleOverlays.Add(1, 2, MaxRowIndex, 4,
[xfpNumberFormat, xfpAlignment], State);
end;
TXLSXFormatParts הוא הסט שמכריע מה שכבת-על תורמת: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment ו-xfpProtection. ציון רק החלקים שאתה מתכוון אליהם הוא מה שמאפשר לשכבות-על להצטבר בהיגיון — שכבת-על עמודה שמספקת פורמט מספר אינה נלחמת בשכבת-על שורה שמספקת מילוי, מפני שאף אחת טוענת לחלק של השנייה
מה בלוק ה-256-שורות נותן לך
מיקומיות. תאים מוחזקים בבלוקים של 256 שורות עם נקודות ידית ציבוריות יציבות, מסודרים row-major, כך שכתיבת גיליון עבודה הולכת בזיכרון בסדר שבו היא תפלוט בייטים במקום לרדוף אחר מצביעים ברחבי הערימה. ידיות יציבות חשובות למשטח ה-API: ידית שקורא מחזיק נשארת תקפה לרוחב הארגון הפנימי מחדש שפריסת הבלוק מבצעת, שזה מה שהופך את הייצוג הקומפקטי לפרט מימוש במקום שינוי פורץ
קומפקט מאגר סגנונות רץ לצידו. לפני כל שמירה, גופנים, מילויים, גבולות, פורמטי מספר, יישורים והגנות שאף תא אינו מפנה אליהם מושמטים. חוברות עבודה ארוכות-חיים מצטברות רשומות סגנון ללא הפניה כפי שמסמכים ארוכי-חיים מצטברים סגנונות שאינם בשימוש, וחוברת עבודה שנערכה על ידי משתמש במשך שעה יכולה לשאת מאות מהם לקובץ שאף אחד לעולם לא יקרא אותם ממנו
שמירה זורמת-שורות, ומתי היא אינה חלה
עם StreamingWrite מופעל — ברירת המחדל — כל שורת גיליון עבודה נכתבת ישירות לתוך זרם ה-deflate של החבילה. החלופה, שזה מה שהדגל מכבה, בונה את ה-XML השלם של גיליון העבודה תחילה ודוחסת אותו לאחר מכן, כך שזיכרון השיא מתכסה עם הגיליון כולו. זרימה הופכת את זה להתכסות עם שורה אחת
מחרוזות משותפות וחלקים עזר פועלים לפי אותו משמעת דרך מסדרה אחת בת-שימוש-חוזר של UTF-8 שפולטת רשומות אחת בכל פעם, מגבילה זיכרון שיא לפי הרשומה הגדולה הבודדת במקום לפי החלק כולו. זה מכסה את טבלת המחרוזות המשותפות ורשומות pivot, שבחוברת עבודה אנליטית רחבה לעיתים קרובות גדולות מכל גיליון עבודה בודד
var
Workbook: TXLSXWorkbook;
begin
Workbook := TXLSXWorkbook.Create(nil);
try
Workbook.Open('ledger-2026.xlsx');
// StreamingWrite defaults to True; turn it off only when a downstream
// step requires the whole worksheet XML to exist before compression
Workbook.StreamingWrite := True;
Workbook.SaveAs('ledger-2026-out.xlsx');
finally
Workbook.Free;
end;
end;
השאר אותו מופעל אלא אם יש לך סיבה קונקרטית שלא. הנתיב הלא-זורם קיים למקרים שבהם משהו אחר בצינור זקוק ל-XML המורכב, ותשלום עבורו כברירת מחדל הוא תשלום עבור מקרה שרוב היישומים לעולם אינם פוגעים בו
איך להגיד האם שכבות-על משמשות בפועל
צפה בספירת התאים, לא בגרף הזיכרון. אם גיליון עבודה מדווח על מספר סביר של תאים פיזיים לאחר שהחלת עיצוב רחב, שכבות-על עושות את עבודתן. אם הספירה קופצת בגודל הטווח המעוצב, משהו בנתיב הקוד מימש את התאים — בדרך כלל לולאה שקוראת כל תא בטווח כדי לבדוק את הסגנון שלו, שכופה פתרון תא-אחד-בכל-פעם ומביס את כל הסידור
פתור סגנון כשאתה זקוק לפורמט האפקטיבי של תא אחד. אל תפתור סגנון עבור מיליון תאים כדי לגלות שלעמודה יש פורמט מספר; שאל את שכבת-העל. אותו כלל חל על כתיבה: הקצה ערכים לתאים שיש להם ערכים, ותן לעיצוב להישאר interval
לאן הזיכרון הנותר הולך
ברגע שתאים וסגנונות קומפקטיים, הצרכנים הגדולים הבאים בחוברת עבודה גדולה הם טבלת המחרוזות המשותפות וחלקי לוויין שהקובץ נושא — מטמוני pivot, שרטוטים, XML שמור מחלקים שמודל האובייקט אינו ממדל. לאלה אסטרטגיות משלהם, והתשובה הכנה היא שאף הגדרה בודדת אינה פותרת את כולם בבת-אחת
אם צוואר-הבקבוק שלך הוא פתיחה במקום שמירה, טעינה סלקטיבית היא המנוף: הסקירה של טעינת מטא-נתונים-בלבד וסלקטיבית של גיליונות עבודה מכסה קריאת חוברת עבודה מבלי לשלם עבור הגיליונות שלא תיגע בהם. ל-throughput של נתיב-הקריאה על קבצים גדולים מאוד, ראה ההערות על ניתוח XLSX מקבילי ומקצה הזיכרון, ולעומסי עבודה לפלט-בלבד שלעולם אינם זקוקים למודל אובייקט כלל, כתיבה זורמת לעבודות אצווה בשרת היא בדרך כלל התאמה טובה יותר מכל כמות של כיוונון כאן
HotXLS קורא וכותב XLS ו-XLSX מקוד דלפי ו-C++Builder מקורי ללא התקנת Excel וללא אוטומציית OLE, מה שהופך את מאפייני הזיכרון הללו לניתנים-לצפייה ולשליטה מלכתחילה — דף רכיב הגיליון האלקטרוני HotXLS מפרט את הפורמטים הנתמכים ואת גרסאות RAD Studio