Convert

Convert XLSX to CSV on Mac (Without Corrupting the Data)

By the Smol team10 min read

To convert XLSX to CSV on a Mac, open the workbook in Numbers and choose File → Export To → CSV, or run soffice --headless --convert-to csv book.xlsx, which took 1.73 seconds here. The conversion is the easy part. What it quietly does to your data is not.

Our test workbook had three sheets, six formulas, and product codes beginning with zeros. The CSV came out with one sheet, no formulas, and — the part that catches people — the codes survived the export intact and were destroyed the moment a spreadsheet opened the CSV again.

What breaks when you convert XLSX to CSV?

Everything below was measured on the same 8,637-byte three-sheet workbook. “Silent” means no warning, no error, exit code 0.

What you hadWhat you getSilent?
3 worksheets1 CSV, first sheet onlyYes
=D2*E2 and SUM()The cached numbers, 59.97 and 1397.97Yes
Currency 4.504.5Yes
SKU 00007 as text00007 in the CSV, then 7 on re-importYes
Grün, Zoë, 東京商事Correct UTF-8, but no byte-order markYes
Cell formatting, widths, coloursGone — CSV has no formattingExpected
Dates as serial 46085Whatever the cell displayedYes
Field containing a commaCorrectly quotedHandled properly
Field containing "quotes"Correctly doubledHandled properly
Field containing a newlineQuoted, spans two physical linesHandled properly

The last three rows are worth saying out loud because the internet is full of warnings about them. LibreOffice’s CSV writer gets commas, quotes and embedded newlines right. The damage is elsewhere.

What happens to the other sheets?

They are dropped. A CSV file is a single table — there is no place in the format for a second sheet — so every converter picks the first one and says nothing. Our Regions and Notes sheets vanished into a successful exit code.

LibreOffice can write all of them, and this is the least-documented useful thing in this whole subject. The CSV filter takes a token string, and its twelfth token is a sheet selector. Set it to -1:

soffice --headless \
  --convert-to 'csv:Text - txt - csv (StarCalc):44,34,76,1,,1033,true,true,true,false,false,-1' \
  --outdir ./out book.xlsx

That produced three files instead of one, named after the sheets: workbook-Orders.csv (459 bytes, 6 rows), workbook-Regions.csv (106 bytes, 4 rows) and workbook-Notes.csv (99 bytes, 2 rows). Nothing else on the Mac does this in one command.

The tokens in order are field separator (44 is a comma), text delimiter (34 is a double quote), character set (76 is UTF-8), first line, column formats, language, quoted-field-as-text, detect special numbers, save cell contents as shown, export cell formulas, remove spaces, and the sheet selector. You will use three of them and ignore the rest.

Do formulas survive the conversion?

No, and usually that is what you want. A CSV row cannot hold a formula, so the exporter writes the cached result. Our =D2*E2 came out as 59.97 and =SUM(F2:F5) as 1397.97. The arithmetic is frozen at the moment of export.

When you actually want the formulas — auditing a workbook, diffing two versions of a model, finding the cell someone hardcoded — flip the tenth token to true:

soffice --headless \
  --convert-to 'csv:Text - txt - csv (StarCalc):44,34,76,1,,1033,true,true,true,true,false,-1' \
  --outdir ./out book.xlsx

The same column then reads:

"00123","Grün GmbH",2026-03-04,3,19.99,"=D2*E2",…
,"TOTAL",,"=SUM(D2:D5)",,"=SUM(F2:F5)",,

One smaller loss in the same column is worth knowing about. A cell holding currency 4.50 exported as 4.5. CSV stores the number, not the two-decimal presentation, so any downstream tool that assumes trailing zeros are meaningful will need the format reapplied.

Why do leading zeros disappear from my CSV?

They do not. This is the single most misdiagnosed problem in spreadsheet work, so here is the measurement. Our SKUs were stored as text: 00123, 00456, 07890, 00007. The exported CSV contained exactly that. Open the CSV in a text editor and the zeros are right there.

Then we converted that CSV back to XLSX and read the cell types out of the XML. Every SKU had become a number: t="n" with values 123, 456, 7890 and 7.

We tested the obvious defence and it fails. Exporting with every text field quoted — "00123" rather than 00123 — still came back as the number 123 on a default import. Quotation marks are a CSV escaping mechanism, not a type annotation, and importers are entitled to ignore them.

Two things do work, both measured:

# 1. Tell the importer that quoted fields are text (7th token)
soffice --headless \
  --infilter='Text - txt - csv (StarCalc):44,34,76,1,,1033,true,false' \
  --convert-to xlsx orders.csv

# 2. Or force column 1 to text with the column-format token (2 = text)
soffice --headless \
  --infilter='Text - txt - csv (StarCalc):44,34,76,1,1/2,1033' \
  --convert-to xlsx orders.csv

Both returned 00123, 00456, 07890 and 00007 as strings. In Excel or Numbers the equivalent is the import dialog: choose Text for that column rather than double-clicking the file. Double-clicking a CSV skips the dialog entirely, which is why it eats your data.

The same mechanism ruins long numeric IDs, which become scientific notation, and anything that looks like a fraction or a date. If the column is an identifier rather than a quantity, it is text, and it has to be declared as text on the way in.

Why does the CSV show Grün instead of Grün?

Because the file is UTF-8 and something read it as Windows-1252. Our export was correct UTF-8, and it had no byte-order mark — the first three bytes were 73 6b 75, the letters sku, with no EF BB BF in front.

Decoding those exact bytes as cp1252 gives the mojibake verbatim: Grün GmbH becomes Grün GmbH. Excel on Windows has historically used the system code page for CSV files that carry no BOM, which is why the same file looks fine on your Mac and wrong on a colleague’s PC.

CJK text does not even get as far as mojibake. Our 東京商事 cell contains byte 0x9D, which cp1252 leaves undefined, so a strict decoder raises an error and a lenient one substitutes replacement characters. There is no version of that story that ends with readable text.

The fix is three bytes:

printf '\xef\xbb\xbf' | cat - orders.csv > orders-bom.csv

415 bytes became 418, and any tool that checks for a BOM — including Excel — now reads UTF-8 without being told. Note that file reports both versions simply as “CSV text”, so use xxd -l 3 to check rather than trusting file.

Why did my dates change format?

Because a date in a spreadsheet is a number with a costume. Internally our order dates were the serials 46085, 46356, 46031 and 46194; what appeared in the CSV was 2026-03-04 and friends, because the cells carried an ISO display style.

Change the cell format in the workbook and the CSV changes with it. A column formatted MM/DD/YY exports as 03/04/26, which is then ambiguous to every importer outside the United States and will be read as 3 April by half of them. If the CSV is going anywhere near another system, set the column to an ISO format before you export. That one action prevents more downstream date bugs than any amount of downstream parsing.

On the way back, the ISO strings were correctly recognised: re-importing gave the same serial numbers, 46085 and the rest. ISO dates round-trip; local formats gamble.

When is Smol not the answer here?

For a single workbook, it is not the answer at all. Numbers is already installed and exports CSV from a menu. If you have LibreOffice, soffice --headless --convert-to csv took 1.73 seconds against Smol’s 11,107 ms, and Smol’s output was byte-identical to it — same 415 bytes, same MD5. Its table conversion is LibreOffice-backed, so you are not buying a different result.

More importantly, the app does not expose the token strings above. No sheet selector, no formula export, no import type forcing. For the multi-sheet and leading-zero problems that make up most of this page, the command line is strictly more capable.

Where it fits:

  • A drawer of mixed files. Table output covers csv tsv json ndjson html md xlsx numbers — including Apple Numbers, which LibreOffice cannot write. The full format reference is here.
  • Right-click in Finder. A Quick Action, no Terminal, no 700 MB office suite installed.
  • Everything else in the same drop. Spreadsheets alongside documents, images and video, one output setting.

Smol is $29 once. For spreadsheet plumbing specifically, be honest with yourself about whether you want a GUI or a pipeline — and if the answer is a pipeline, the CSV to JSON route has the type problems worth reading about.

How this was measured

MacBook Pro (Mac14,9), Apple M2 Pro, 10 cores, 16 GB, macOS 26A428. LibreOffice 26.2.3.2 (AARCH64), Smol 1.0.34. Timings from /usr/bin/time -p, wall-clock real; Smol’s figure is its own reported job duration. Cell types and values were read directly out of the XLSX XML with unzip -p rather than inferred from a spreadsheet’s display.

The workbook was built for this article and taken from nowhere: a flat-ODS file written by hand, converted to XLSX by LibreOffice, containing three sheets, four text SKUs with leading zeros, four date cells with an ISO display style, six live formulas with cached values, four non-ASCII customer names, and single fields carrying an embedded comma, embedded double quotes and an embedded newline. Every fixture was deleted afterwards.

Frequently asked questions

How do I convert XLSX to CSV on a Mac?

Open the workbook in Numbers and choose File → Export To → CSV, or run soffice --headless --convert-to csv book.xlsx if you have LibreOffice, which took 1.73 seconds on our three-sheet test file. Both export the first worksheet only, because a CSV file can hold exactly one table.

How do I convert all sheets in a workbook to separate CSV files?

Use the LibreOffice CSV filter token string with the sheet selector set to -1: soffice --headless --convert-to "csv:Text - txt - csv (StarCalc):44,34,76,1,,1033,true,true,true,false,false,-1" book.xlsx. Our three-sheet workbook produced workbook-Orders.csv, workbook-Regions.csv and workbook-Notes.csv, named after the sheets.

Why did my leading zeros disappear when I converted XLSX to CSV?

They did not disappear in the CSV. Our SKUs 00123 and 00007 were present in the exported file. They vanished when a spreadsheet re-opened it, because CSV import applies type inference and reads 00007 as the number seven. Quoting the field does not prevent this; you have to declare the column as text on import.

Do formulas survive an XLSX to CSV conversion?

No. CSV has no formula concept, so the exporter writes the cached result: our =D2*E2 became 59.97. If you want the formula text instead, set the tenth token of the LibreOffice CSV filter to true and the same cell exports as "=D2*E2". That is useful for auditing a workbook rather than consuming its numbers.

Why does my CSV show Grün instead of Grün?

The file is UTF-8 with no byte-order mark, and something read it as Windows-1252. Decoding our exported bytes as cp1252 produces exactly Grün GmbH. Adding the three-byte BOM with printf "\xef\xbb\xbf" and prepending it fixes Excel on Windows, turning a 415-byte file into 418 bytes.

Keep reading