DEV Community

Anuj Kabra
Anuj Kabra

Posted on

Opening an .xlsx on a machine with no Excel

Someone sends a spreadsheet and the machine in front of me has no Excel: a Linux laptop, a borrowed computer, a fresh VM. I don't need to edit the model, I just need to read the numbers. Here are the free ways I open it, what an .xlsx actually is underneath, and the one check worth doing before you pick a tool: is it really .xlsx, or the older .xls?

Everything below was tested on a small sample workbook (two sheets, one formula) with LibreOffice 25.2 and Python 3 on Linux.

First, check which format you have

.xlsx and .xls look alike in a file name, but they are different formats:

  • .xlsx (Excel 2007 and later) is a ZIP archive full of XML files.
  • .xls (older Excel) is a binary format inside a Microsoft compound file container. It is not a ZIP.

The extension can lie, because people rename files. The first few bytes don't. On Linux or Mac:

od -A n -t x1 -N 8 report.xlsx
Enter fullscreen mode Exit fullscreen mode

On my two test files this printed:

 50 4b 03 04 14 00 00 00    <- .xlsx: starts with "PK", the ZIP signature
 d0 cf 11 e0 a1 b1 1a e1    <- .xls: the compound file signature
Enter fullscreen mode Exit fullscreen mode

If you see 50 4b 03 04, it's a ZIP, so it's .xlsx (or another Office Open XML file). If you see d0 cf 11 e0, it's the old binary format. Most of the tools below open both, but scripts and libraries often handle only one, so this saves a confusing error later.

Option 1: LibreOffice

LibreOffice is free and opens .xlsx and .xls on Linux, Mac and Windows. Double-click the file or open it from LibreOffice Calc. Every sheet shows as a tab, and formulas are recalculated when the file opens.

If I only want the data as text, it also works from the command line without opening a window:

soffice --headless --convert-to csv report.xlsx
Enter fullscreen mode Exit fullscreen mode

That writes report.csv next to the file, but only from the first sheet. On my LibreOffice 25.2, this longer form wrote one CSV per sheet (report-Invoices.csv, report-Notes.csv):

soffice --headless --convert-to 'csv:Text - txt - csv (StarCalc):44,34,UTF8,1,,0,false,true,false,false,false,-1' --outdir out report.xlsx
Enter fullscreen mode Exit fullscreen mode

The -1 at the end means "all sheets". Older LibreOffice versions export only the first sheet whatever you pass, so check the output folder.

Option 2: Google Sheets

Upload the file to Google Drive and open it with Google Sheets. It reads .xlsx and .xls. The catch is in the first word: the file goes to Google's servers. For a team lunch list that's fine. For payroll, customer data or anything under an NDA, use one of the local options instead.

Option 3: Look inside the ZIP

Because an .xlsx is a ZIP, you can list its contents with nothing but unzip:

unzip -l report.xlsx
Enter fullscreen mode Exit fullscreen mode

On my sample the important entries were:

xl/workbook.xml              <- sheet names and order
xl/worksheets/sheet1.xml     <- the cells of the first sheet
xl/worksheets/sheet2.xml
xl/styles.xml                <- number formats, fonts, colours
[Content_Types].xml
Enter fullscreen mode Exit fullscreen mode

To read a sheet directly:

unzip -p report.xlsx xl/worksheets/sheet1.xml
Enter fullscreen mode Exit fullscreen mode

Each cell is a <c> element with its address (r="B2") and value (<v>120</v>). Text cells are often stored once in xl/sharedStrings.xml and referenced by number, so in files saved by Excel or LibreOffice you may see t="s" and a small number where you expected a word. That number is an index into the shared strings list.

This route is clumsy for reading a whole table, but it's useful when a file won't open anywhere, or when you want to see sheet names, hidden sheets (state="hidden" in workbook.xml) or whether a cell holds a formula (<f>) before trusting it.

Option 4: A few lines of Python

If I want the values in a script, openpyxl reads .xlsx (not .xls):

from openpyxl import load_workbook

wb = load_workbook("report.xlsx", data_only=True, read_only=True)
for ws in wb.worksheets:
    print("==", ws.title)
    for row in ws.iter_rows(values_only=True):
        print(row)
Enter fullscreen mode Exit fullscreen mode

pip install openpyxl first. For an old .xls, convert it with the LibreOffice command above, or use a library that supports the binary format, such as xlrd.

The formula gotcha

An .xlsx stores two things for a formula cell: the formula, and the last value Excel calculated for it (the cached value). Viewers that don't run formulas show the cached value.

That's normally what you want, but it can mislead you. In my sample, made with a script, the total cell held <f>SUM(B2:B3)</f> and an empty <v></v>, because no spreadsheet program had ever calculated it. LibreOffice recalculated it and showed 200. openpyxl with data_only=True gave None for that cell. If a total looks blank or wrong in a viewer, open the file in LibreOffice, which recalculates, before assuming the data is missing.

Which one I use

  • Just reading, sensitive data: LibreOffice, or a local viewer.
  • Just reading, nothing sensitive: Google Sheets is quickest if I'm already signed in.
  • Pulling the data into something else: the soffice --convert-to csv command or openpyxl.
  • A file that won't open: od to check the format, then unzip -l to see if it's a damaged ZIP.

I built ToolOcean, and it has an Excel viewer page that reads .xlsx and .xls in the browser without uploading the file. It shows the cached cell values rather than recalculating formulas, so the gotcha above applies there too. It's at toolocean.co/excel-reader.

Drafted with AI help, tested and checked by me.

Top comments (0)