Skip to content
datahacks.dev
Excel

XLS Versus XLSX Versus XLSB and Which to Hand Over

Compare the XLS, XLSX and XLSB Excel formats on limits, size, macros and library support, detect which one a file really is, and pick one to hand over.

8 min read
Summarize in AI
Flat illustration of two options weighed side by side.

Three file extensions cover almost every Excel file you will be given, and they are not interchangeable. A system that exports .xls, a colleague who saves .xlsb to make a heavy file smaller, and a script that expects .xlsx will each cause a different error, and the error messages rarely mention the format. This post compares the three on the properties that matter for moving data, shows how to tell what a file really is regardless of its name, and ends with a recommendation for what to send. It is part of the Excel cluster that starts with what is inside an Excel file.

The three formats

XLS is the Excel 97 to 2003 format, a binary container (OLE2, also called Compound File Binary) holding a stream of records. It has 65,536 rows and 256 columns per sheet, and its limits on styles and colors are far lower than modern Excel. It cannot be read by unzipping. Many old systems, report generators and instruments still write it because the library they use was written in 1999.

XLSX is the Office Open XML format introduced with Excel 2007: a zip of XML parts, 1,048,576 rows and 16,384 columns, no macros. It is an open standard (ECMA-376 and ISO/IEC 29500), and you can read it with an archive tool and a text editor. It is the format almost every library targets first.

XLSB is the Excel binary workbook: also a zip package, but the parts are binary records and not XML. It stores the same data as XLSX with the same limits, usually in a smaller file that Excel loads faster, since parsing binary records is cheaper than parsing XML. It can hold macros. Its drawback is support: Excel and a few libraries read it, and many tools do not.

There is a fourth member of the family worth naming. XLSM is the XLSX layout with macros enabled. It is XML, a zip, and as readable as XLSX, but a vbaProject.bin part carries the code, and many mail filters block it.

PropertyXLSXLSXXLSB
ContainerOLE2 binaryZip of XMLZip of binary parts
Rows by columns65,536 by 2561,048,576 by 16,3841,048,576 by 16,384
MacrosYesNo (use XLSM)Yes
Typical sizeLarger than XLSX for the same dataBaselineOften the smallest
Readable without ExcelHardEasyHard
Library supportxlrd, calamineNearly everythingpyxlsb, calamine

How the libraries treat each one

Support differs more than the table suggests, and this is where handovers fail. In Python:

  • openpyxl reads and writes XLSX and XLSM. It does not read XLS or XLSB, and raises an error for them.

  • xlrd reads XLS. Version 2.0 and later dropped XLSX support, so it is only for the old format.

  • pyxlsb reads XLSB, with fewer features than openpyxl has for XLSX.

  • python-calamine is a Rust-based reader that opens XLS, XLSX, XLSB and ODS through one interface. pandas can use it as engine="calamine" in recent versions (choosing between openpyxl, xlsxwriter and calamine).

  • pandas picks an engine from the extension when it can, and fails with an instruction to install one when it cannot (fixing the missing optional dependency openpyxl error).

DuckDB's Excel extension reads XLSX. The database import wizards of SQL Server and MySQL Workbench have their own rules, and some need a driver for the format: SQL Server's tooling relies on the Access Database Engine (ACE) provider for XLSX and XLSB.

In practice, if your pipeline is written with openpyxl and someone sends an .xlsb, the pipeline stops. Converting the file once in Excel (Save As, XLSX) is the fastest fix, and the permanent fix is to agree on a handover format.

Do not trust the extension

Files get misnamed constantly. A web application "exports to Excel" by returning an HTML table with an .xls extension, because Excel opens it. Another writes a tab-separated text file and calls it .xls. A script renames a CSV. Excel opens all of these, with a warning that the file format and extension do not match, and every library fails differently. Check the first bytes of the file:

Python
import sys, zipfile

SIGNATURES = {
    b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1": "OLE2 binary (real XLS, or an old DOC or PPT)",
    b"PK\x03\x04": "zip package (XLSX, XLSM, XLSB, ODS or another zip)",
}

path = sys.argv[1]
with open(path, "rb") as f:
    head = f.read(8)

kind = next((v for k, v in SIGNATURES.items() if head.startswith(k)), None)
if kind is None:
    text = head.lstrip(b"\xef\xbb\xbf \r\n\t")[:1]
    kind = "HTML or XML text" if text == b"<" else "plain text (CSV or TSV?)"

print(path, "->", kind)

if head.startswith(b"PK"):
    names = zipfile.ZipFile(path).namelist()
    if "xl/workbook.bin" in names:
        print("   workbook.bin present: XLSB")
    elif "xl/workbook.xml" in names:
        macros = "xl/vbaProject.bin" in names
        print("   workbook.xml present: XLSX" + (" with macros (XLSM)" if macros else ""))

The eight-byte OLE2 signature D0 CF 11 E0 A1 B1 1A E1 marks a binary XLS. Every zip begins with PK. Inside a zip, xl/workbook.xml marks the XML flavor and xl/workbook.bin marks XLSB. A file that starts with < is HTML or XML, and one that starts with something else printable is probably delimited text. For a web "export" renamed to .xls, pandas can read it with read_html, not read_excel, and the quicker fix is to ask the source for a CSV.

Which to hand over

The choice depends on who reads the file.

A person with Excel. Send XLSX. Every version since 2007 opens it, Google Sheets and LibreOffice open it, and macros are not an issue. Choose XLSB only for an internal file that is slow to open, where everyone who touches it uses desktop Excel, and keep an XLSX copy for anyone else.

A program written in any language. Send CSV if the data is one table, and see Excel versus CSV for sharing data with other systems. If styles, several sheets or formulas matter, send XLSX with the data on a single sheet, a header in row 1 and identifiers stored as text.

An archive. XLSX, with the data also exported as CSV. The XML layout is documented and readable in the long term, which cannot be said for the binary formats.

A legacy system that demands XLS. Send XLS, keep it under 65,536 rows, and make sure no more than 256 columns exist. Write it with a library, not by renaming a CSV, and test the file in the receiving system. Treat it as a bridge that you plan to retire.

Never hand over XLSM unless the receiver asked for the macros. A file with macros is blocked by many filters and treated with suspicion by good habit.

Converting between them

Excel's Save As converts one to another and is the most faithful option for a single file. For many files, LibreOffice can run without a window:

Shell
soffice --headless --convert-to xlsx --outdir converted old/*.xls

The command reads every .xls file in old and writes a .xlsx file for each into converted. Conversion is not lossless. Formatting that has no equivalent is approximated, charts can change, and formulas are recalculated by the converting program, which may differ from Excel in edge cases. Compare totals before and after, as in validating row counts after an Excel import. With python-calamine you can skip the conversion and read the old file directly, which avoids the question for data-only use.

Be careful about the direction. Converting XLSX to XLS to satisfy an old system silently drops every row after 65,536 in some tools, and fails in others. Check the row count first.

Size and speed in practice

XLSB is often described as 50 to 70 percent smaller than XLSX and several times faster to open. Claims like these come from vendor guidance and depend heavily on the content: a workbook that is mostly text compresses well in either, and one with large formula chains gains the most from the binary layout. I have not measured them here. If you are considering XLSB for speed, convert a representative file and time the open in Excel and the load in your pipeline. XLSX already compresses with zip, so the gap is smaller than the gap between XLS and XLSX was.

The reverse matters more for data engineering: XLSX parsing is slow because XML parsing is slow, and a million-row file takes noticeable time in any library. A fast reader helps (reading Excel files faster with python calamine), and so does reading in a mode that streams instead of loading everything (reading large Excel files in read-only mode).

When none of the three Excel formats is the right handover

The data is more than a million rows. All three formats stop at 1,048,576 rows. Use CSV, a database or a columnar file such as Parquet (Excel row and column limits).

The receiver is a machine and the data has types. A spreadsheet carries only numbers, strings, booleans and dates-as-numbers. Typed formats such as Parquet or Avro, or JSON with a schema, say more.

You need version control. Binary and zipped formats diff poorly. Keep the source as CSV in the repository and generate workbooks from it.

The file is a template with logic. Formulas and macros that the receiver must keep working are a reason for XLSX or XLSM, but also a reason to ask whether the logic belongs in code.

The structure of the package formats is described in ECMA-376, and the binary workbook format in Microsoft's MS-XLSB specification. For a quick look at a small table as JSON, use the CSV to JSON converter.

Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.