How to Convert a Markdown Table to CSV Without Breaking Your Columns
Turn a GitHub-flavoured Markdown pipe table into clean RFC 4180 CSV: drop the separator row, split on pipes, quote cells with commas, import into Excel.
How to Convert a Markdown Table to CSV Without Breaking Your Columns
A Markdown table is great to read in a README and useless the moment you want to sort it, sum a column, or load it into pandas. The fix sounds trivial: strip the pipes, write commas, done. It is not trivial. A cell with a comma in it, a literal vertical bar, or a ragged row will quietly corrupt the output, and you will not notice until a spreadsheet shifts every value one column to the left.
This post walks through exactly what a correct conversion has to do, with a worked example you can check by hand. If you just want the result, paste your table into Markdown Table to CSV and download the file. If you want to know why naive converters fail, read on.
What a Markdown table actually looks like
GitHub-flavoured Markdown (GFM) tables have three parts. The first line is the header. The second line is an alignment separator made of dashes and optional colons. Everything after that is data. Here is a small one:
| Tool | Type | Price/min |
| --------- | --------- | ------------- |
| /v1 \| /v2 | converter | 1,000 req/min |
| ascii-art | generator | 50 req/min |
Three things in that table will trip up a quick-and-dirty script. The second line is not data, it is formatting. The cell /v1 \| /v2 contains an escaped pipe that must stay one column. And 1,000 req/min contains a comma, which is the very character CSV uses to split fields.
Step one: drop the separator row
The dashed line — | --------- |, | :---: |, or | ---: | — is GFM's alignment marker. It tells a renderer whether to left, center, or right align each column. It is never data.
The rule is simple to state and easy to get wrong: a separator row is any row whose every cell, after trimming, contains only dashes and optional colons. Detect it, remove it, and a four-line Markdown table (header, separator, two data rows) becomes a three-row CSV. Skip this check and your CSV gets a phantom row of dashes wedged between the header and the first record, which then breaks any importer expecting a clean header.
Step two: split on pipes, but only the unescaped ones
Splitting on | is where most converters fall apart. Markdown lets you put a literal vertical bar inside a cell by escaping it as \|. In the example above, /v1 \| /v2 is one cell that should read /v1 | /v2.
A converter that splits on every | reads that cell as two columns, /v1 and /v2, and now the whole row has one column too many. Every value after it lands under the wrong header. The correct approach is to split only on pipes that are not preceded by a backslash, then turn each surviving \| back into a single |. You also strip the optional leading and trailing pipes that GFM allows, so | a | b | and a | b both yield two cells, not four with two empties.
Step three: quote cells that would corrupt the CSV
CSV has its own escaping rules, and they are the whole reason RFC 4180 exists. Three characters inside a cell force quoting:
- the delimiter itself (a comma, in standard CSV),
- a double quote,
- a line break.
When any of those appear, the cell is wrapped in double quotes, and any embedded double quote is doubled. So said "ok" becomes "said ""ok""". The cell 1,000 req/min becomes "1,000 req/min". This is exactly what Excel, Google Sheets, and pandas expect on the way back in — quote the field, escape inner quotes by repeating them, and the field count stays correct no matter what is inside.
Get this wrong and the comma in 1,000 reads as a field boundary, the row gains a column, and your spreadsheet quietly misaligns. It is the single most common silent failure in homemade table conversions.
A worked example
Take the table from earlier and run all three steps. After dropping the separator row, splitting on unescaped pipes, and quoting the cells that need it, you get:
Tool,Type,Price/min
/v1 | /v2,converter,"1,000 req/min"
ascii-art,generator,50 req/min
Read it back: three columns, three rows. The escaped pipe collapsed to a literal | and stayed inside one field. The comma in 1,000 req/min is safe because the field is quoted. The two simple cells were left unquoted because they did not need it — RFC 4180 only quotes when necessary, which keeps the file small and human-readable.
Bringing it into a spreadsheet
Once you have the CSV, opening it should be a double click. Two details matter for that to actually work.
First, non-ASCII text. If your table has Chinese names, accented characters, or emoji, the file needs a UTF-8 byte-order mark so Excel reads the encoding correctly on a double click instead of mangling it into mojibake. The download from the tool writes that BOM for you.
Second, the delimiter. Many European and localized Excel installs split CSV on semicolons, not commas. If you open a comma CSV and every column lands inside a single cell, you do not have a broken file — you have the wrong delimiter for that locale. Switch the export to semicolon before downloading and the columns line up without touching the text-import wizard. Tab-separated output is there too, which pastes cleanly into a sheet directly.
Why I keep this in the browser
I spend a lot of my day moving tables between docs, chat assistants, and data tools, and the tables almost always carry something I would rather not upload — internal rate limits, a list of customer emails, ticket numbers from a wiki. A converter that posts my paste to a server is a non-starter for that work, no matter how convenient the UI is.
So this conversion runs entirely as JavaScript in the page. The Markdown you paste never leaves the tab and never gets written into the shareable URL. Only the cosmetic options — the delimiter and the convert-every-table switch — are stored in the link, so you can share your settings without leaking a single cell of data. That is the difference between a tool I will paste a production config into and one I will not.
The reverse trip and other table formats
Conversion goes both ways. If you have CSV and want a clean Markdown table to drop into a README or a pull request, CSV to Markdown Table does the inverse — it builds the pipe table, adds the alignment row, and pads ragged rows so every column lines up in monospace.
Between the two you can round-trip a table through whatever format the moment needs: Markdown for a doc, CSV for a spreadsheet or a script, and back again, without ever retyping a cell or trusting a server with your data.
Made by Toolora · Updated 2026-06-13