HotXLS เปิดสมุดงานที่เขียนโดย Excel 2.0, 3.0 และ 4.0 ได้โดยตรงจาก Delphi และ C++Builder ไฟล์เหล่านี้มีมาก่อนคอนเทนเนอร์เอกสารประกอบแบบ OLE ที่ .xls รุ่นหลังทั้งหมดใช้ ดังนั้นจึงเป็นสตรีมระเบียน BIFF ดิบที่ไม่มีตัวห่อหุ้มสตอเรจใดๆ เลย และตัวอ่านที่สร้างมาสำหรับ BIFF8 จะหาโครงสร้างที่จำได้แม้แต่ชิ้นเดียวภายในไฟล์เหล่านี้ไม่พบ การเปิดไฟล์หนึ่งใช้การเรียก Open แบบเดียวกับสมุดงานอื่นๆ ตัวอ่านจะตรวจจับรูปแบบและสลับเส้นทางเอง
ไฟล์เหล่านี้ยังคงปรากฏขึ้นอยู่เสมอ ซึ่งเป็นเหตุผลเดียวที่เรื่องทั้งหมดนี้สำคัญ คลังเก็บทางวิศวกรรม การเก็บรักษาบันทึกภาครัฐ ข้อมูลห้องปฏิบัติการจากเครื่องมือที่ซอฟต์แวร์ควบคุมเขียนขึ้นในปี 1993 และระบบบัญชีที่ทำงานมายาวนาน ล้วนทิ้งสมุดงาน BIFF2 และ BIFF4 ไว้เบื้องหลัง Excel สมัยใหม่ปฏิเสธที่จะเปิดไฟล์เหล่านี้หลายไฟล์โดยสิ้นเชิง เพราะได้ถอดตัวแปลงไฟล์เก่าออกไปด้วยเหตุผลด้านความปลอดภัย ทำให้เหลือชุดข้อมูลที่ไม่มีใครอ่านได้ด้วยเครื่องมือที่ใครก็มีอยู่แล้ว
อะไรทำให้สมุดงานยุคก่อน OLE แตกต่างออกไป
ทุก .xls ตั้งแต่ Excel 5.0 เป็นต้นไปเป็นไฟล์ประกอบแบบ OLE2 ซึ่งเป็นระบบไฟล์ขนาดเล็กภายในไฟล์เดียว โดยสมุดงานอยู่ในสตรีมที่ชื่อ Workbook หรือ Book การแยกวิเคราะห์ไฟล์หนึ่งเริ่มต้นด้วยการแยกวิเคราะห์คอนเทนเนอร์นั้นก่อน ตามที่อธิบายไว้ในรูปแบบไบนารีไฟล์ประกอบใน Pascal
BIFF2 ถึง BIFF4 ไม่มีคอนเทนเนอร์เลย ไฟล์เริ่มต้นทันทีด้วยระเบียน BOF และหมายเลขระเบียนของ BOF นั้นเข้ารหัสรุ่นไว้ คือ $0009 สำหรับ BIFF2, $0209 สำหรับ BIFF3 และ $0409 สำหรับ BIFF4 HotXLS ตรวจสอบความยาวเนื้อหาของ BOF ซึ่งอยู่ระหว่างสี่ถึงหกไบต์ และประเภทซับสตรีม คือ $0010 สำหรับเวิร์กชีต $0020 สำหรับแผนภูมิ และ $0040 สำหรับชีตมาโคร ก่อนที่จะยึดมั่นกับเส้นทางดิบ การตรวจสอบนี้เองที่ป้องกันไม่ให้ไฟล์ที่เสียหายหรือระบุผิดถูกตีความว่าเป็นสมุดงานเก่ามาก
สามรุ่น สามผังระเบียน
ระเบียนเซลล์คือจุดที่แต่ละรุ่นแตกต่างกันเห็นได้ชัดที่สุด BIFF2 ครอบครองบล็อกหมายเลขระเบียนต่ำที่ต่อเนื่องกัน คือ $0001 ถึง $0005 สำหรับเซลล์ว่าง จำนวนเต็ม ตัวเลข ป้ายชื่อ และบูลีนหรือข้อผิดพลาด และแต่ละเนื้อหาพกฟิลด์แอตทริบิวต์สามไบต์ในจุดที่รุ่นหลังใส่ดัชนีรูปแบบขยาย BIFF3 และ BIFF4 ทิ้งวิธีนั้นและนำหมายเลขระเบียนกับผังของ BIFF5 มาใช้ซ้ำ คือ $0201, $0203, $0204 และ $0205 พร้อมดัชนี XF สองไบต์
รายละเอียดสุดท้ายนี้ก่อให้เกิดความล้มเหลวเฉพาะแบบที่วินิจฉัยผิดได้ง่าย ระเบียน LABEL ของ BIFF3 หรือ BIFF4 มีโครงสร้างเหมือนกับคู่ของมันใน BIFF5 ทุกประการ คือแถวและคอลัมน์ตามด้วยดัชนีรูปแบบแล้วจึงเป็นจำนวนอักขระ หากเขียนตัวอ่านที่สมมติผังแบบ BIFF2 มันจะอ่านขาดไปสองไบต์ แล้วเดินออกนอกท้ายระเบียนและตีความทุกอย่างหลังจากนั้นผิดพลาด อาการที่เกิดขึ้นไม่ใช่ข้อยกเว้น แต่เป็นสมุดงานที่อ่านได้พร้อมขยะที่ดูสมเหตุสมผลอยู่ในนั้น
ระเบียนสูตรครองการนับหมายเลขคู่ขนานกันทั้งสามรุ่น คือ $0006, $0206 และ $0406 เมื่อสูตรให้ผลลัพธ์เป็นสตริง สตริงนั้นจะมาถึงในระเบียนถัดไปแยกต่างหาก คือ $0007 หรือ $0207 และรูปแบบ BIFF2 ของมันใช้พรีฟิกซ์ความยาวหนึ่งไบต์แทนที่จะเป็นสองไบต์ที่ใช้ในภายหลัง
เหตุใดสูตรจึงกลับมาเป็นค่า ไม่ใช่ข้อความ
HotXLS อ่านผลลัพธ์ที่แคชไว้ของสูตรในไฟล์เหล่านี้ และไม่พยายามสร้างนิพจน์สูตรขึ้นใหม่ นี่คือขอบเขตที่จงใจไว้ ไม่ใช่ช่องว่างที่รอการเติมเต็ม
นิพจน์ที่แยกวิเคราะห์แล้วใน BIFF2 ถึง BIFF4 ใช้การเข้ารหัสโทเคนที่แตกต่างจาก BIFF5 และรุ่นหลังในแบบที่มากกว่าเรื่องความสวยงาม คือความยาวโทเคนถูกใส่พรีฟิกซ์ต่างกัน โทเคนอ้างอิงมีขนาดต่างกัน และตารางดัชนีฟังก์ชันถูกเปลี่ยนหมายเลขระหว่างแต่ละรุ่น การนำไบต์เหล่านั้นผ่านตัวแปลนิพจน์ของ BIFF8 ไม่ได้สร้างสูตรที่ผิด แต่สร้างสูตรที่สุ่มขึ้นมา การอ่านค่าที่แคชไว้ให้คุณได้ตัวเลขหรือสตริงที่ Excel คำนวณครั้งล่าสุด ซึ่งเป็นสิ่งที่การย้ายข้อมูลคลังเก็บต้องการจริงๆ
ค่าที่แคชไว้อยู่ที่ออฟเซ็ตซึ่งขึ้นกับรุ่นภายในระเบียน คือไบต์ที่ 7 สำหรับ BIFF2 และไบต์ที่ 6 สำหรับ BIFF3 และ BIFF4 ค่าพิเศษ สตริง บูลีน ข้อผิดพลาด และค่าว่าง ถูกเข้ารหัสด้วยเวิร์ดเครื่องหมาย $FFFF พร้อมตัวแยกแยะ ซึ่งเป็นข้อตกลงเดียวกับที่ BIFF รุ่นหลังยังคงไว้
การเปิดไฟล์หนึ่งไฟล์
โค้ดที่เรียกใช้งานไม่มีอะไรพิเศษ ซึ่งเป็นประเด็นสำคัญ การตรวจจับเกิดขึ้นภายใน Open:
uses
lxHandle;
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
R, C: Integer;
V: Variant;
begin
Book := TXLSWorkbook.Create;
try
if Book.Open('archive\1993-inventory.xls') <> 1 then
begin
Writeln('unreadable - quarantine for manual review');
Exit;
end;
Sheet := Book.Sheets[1]; // Sheets[] เริ่มนับจากหนึ่ง
for R := Sheet.UsedRange.FirstRow + 1 to Sheet.UsedRange.LastRow + 1 do
for C := Sheet.UsedRange.FirstCol + 1 to Sheet.UsedRange.LastCol + 1 do
begin
V := Sheet.Cells[R, C].Value;
if not VarIsEmpty(V) then
Writeln(Format('R%dC%d = %s', [R, C, VarToStr(V)]));
end;
finally
Book.Free;
end;
end;
สังเกตเลขคณิตของดัชนีในลูปนั้น ขอบเขตของ UsedRange เริ่มนับจากศูนย์ ในขณะที่ทั้งคอลเลกชันชีตและการเข้าถึงเซลล์เริ่มนับจากหนึ่ง ซึ่งเป็นความไม่สอดคล้องกันที่มีมาก่อน API ปัจจุบัน และถูกเก็บรักษาไว้เพื่อความเข้ากันได้ การลืมปรับค่านี้จะตรวจสอบสี่เหลี่ยมผิดจุด และรายงานว่าไม่มีอะไรผิดปกติในระหว่างนั้น การตรวจสอบเบื้องต้นแบบประหยัดที่หลีกเลี่ยงการโหลดไฟล์ทั้งหมดอธิบายไว้ในการตรวจสอบสมุดงานแบบเบา
สิ่งที่คุณจะไม่ได้รับ และควรทำอย่างไรกับมัน
รูปแบบไม่ถูกตีความ HotXLS ไม่แยกวิเคราะห์ระเบียน XF และ FONT ของรุ่นเหล่านี้ ดังนั้นฟอนต์ สี เส้นขอบ และรูปแบบตัวเลขจึงไม่มีให้ใช้งาน และเซลล์ที่ Excel เคยแสดงเป็นวันที่จะกลับมาเป็นหมายเลขลำดับดิบของมัน
ปัญหาสุดท้ายนี้ต้องจัดการในโค้ดของคุณเองแทนที่จะจัดการในตัวอ่าน และเหตุผลก็ตรงไปตรงมา รูปแบบตัวเลขใน BIFF2 ถึง BIFF4 ไม่น่าเชื่อถือพอที่จะขับเคลื่อนการตัดสินใจเรื่องวันที่โดยอัตโนมัติ คอลัมน์ของตัวเลขห้าหลักอาจเป็นวันที่ หรืออาจเป็นหมายเลขชิ้นส่วนก็ได้ ให้แปลงอย่างจงใจ โดยใช้ระบบวันที่ของสมุดงาน ซึ่งกฎของมันอธิบายไว้ในหมายเลขลำดับวันที่ ระบบปี 1904 และรูปแบบตัวเลข:
// ตัดสินใจต่อคอลัมน์ ไม่ใช่ต่อค่า ตัวเลขห้าหลักอาจเป็น
// วันที่หรือหมายเลขชิ้นส่วนก็ได้ และรูปแบบเก่าจะไม่บอกคุณ
if ColumnHoldsDates(C) then
begin
// ระบบวันที่ทั้งสองห่างกัน 1462 วัน ดังนั้นค่าลำดับเดียวกัน
// จึงหมายถึงสองวันที่ที่ห่างกันสี่ปี ให้อ่านระบบจากสมุดงาน
// แทนที่จะสมมติเอาเอง
if Book.Date1904 then
Writeln(DateToStr(SerialToDate1904(V)))
else
Writeln(DateToStr(SerialToDate1900(V)));
end
else
Writeln(VarToStr(V));
ข้อสังเกตเชิงโครงสร้างสองข้อทำให้ภาพรวมสมบูรณ์ ระเบียนการป้องกันด้วยรหัสผ่านและรหัสหน้ารหัสปรากฏอยู่ภายในสตรีมเวิร์กชีตเดี่ยว แทนที่จะอยู่ในสตรีมระดับสมุดงาน เพราะไม่มีสตรีมระดับสมุดงานให้ใส่มันไว้ จึงต้องรู้จำในบริบทของเวิร์กชีต และไฟล์ BIFF2 ถึง BIFF4 มีซับสตรีมชีตเพียงหนึ่งเดียวพอดี สมุดงานแบบหลายชีตไม่มีอยู่จนกว่ารูปแบบไฟล์จะได้คอนเทนเนอร์ของมัน
เส้นทางการย้ายข้อมูลเชิงปฏิบัติจึงเป็นสองขั้นตอน อ่านไฟล์เก่าเพื่อเอาค่าของมัน แล้วเขียนสมุดงานสมัยใหม่ที่พกค่าเหล่านั้นพร้อมรูปแบบที่คุณกำหนดเอง การอ่านไฟล์เก่า การเขียนแบบสมัยใหม่ และทุกอย่างในระหว่างนั้นทำงานอยู่ในไลบรารีเดียวกันสำหรับ Delphi และ C++Builder อธิบายไว้ที่หน้าคอมโพเนนต์สเปรดชีต HotXLS สำหรับ Delphi