Skip to content
datahacks.dev
CSV

Excel CSV Versus RFC CSV Differences Worth Knowing About

Where Excel's CSV departs from RFC 4180: delimiter, encoding, BOM, the sep line, displayed values, formulas and the ID trap, plus a converter script.

7 min read
Summarize in AI
Flat illustration of a spreadsheet grid with a selected cell and a formula bar.

People say "CSV" and mean three things: the format in RFC 4180, the format Excel writes, and the format their library reads. Most of the time the three agree, which is why the differences are a surprise when they do not. The RFC is a short document that describes common practice and does not claim authority over any program. Excel is a program with its own habits, shaped by decades of regional settings, and it is the writer of a large share of the CSV files in the world. If your pipeline reads files that people export from Excel, the differences are not trivia. They are the list of things your reader has to handle or reject. For the broader family of variants see CSV dialects and variants you will run into, and for the standard itself what RFC 4180 says about CSV files.

The differences side by side

AspectRFC 4180What Excel does
DelimiterCommaThe system list separator: comma or semicolon, by region
Encodingcharset parameter, US-ASCII by default"CSV" writes a legacy code page; "CSV UTF-8" writes UTF-8 with a byte order mark
First lineAn optional headerTreated as data when opened; may be a sep= instruction
Line endCRLFCRLF between rows, a bare LF for a line break inside a cell
QuotingFields with comma, quote or line breakThe same, with the regional delimiter as the trigger
ValuesTextWritten as displayed: formatted dates, percentages, thousands separators
FormulasNot a conceptEvaluated on open; saved as their results
Empty rows and columnsNot mentionedWritten for the whole used range
Cell lengthNo limit32,767 characters per cell
Row widthSame number of fields on every linePadded to the widest row

The sections below take the ones that cause real trouble.

The delimiter belongs to the machine

The first row is the one that causes the most support tickets. An RFC reader expects a comma. Excel writes whatever the Windows or macOS regional setting names as the list separator, and in much of Europe and Latin America that is a semicolon, because those regions use a comma for decimals. A German Excel therefore writes id;name;amount and a decimal like 1234,50 with no quotes, since a comma is not special when the delimiter is a semicolon. That also means the number format has changed: the same file read as comma-delimited would split 1234,50 into two fields. Opening a semicolon separated CSV in Excel covers the reading side, and comma, semicolon, tab or pipe the choice.

For a pipeline, the lesson is that "Excel CSV" is at least two dialects. Ask the sender's region, or detect from the first line, and record it in the feed's configuration. Do not assume the next file will match the last when a different person exports it from a different laptop.

Encoding and the byte order mark

Excel's plain "CSV (Comma delimited)" writes the system's legacy code page, and anything that does not fit becomes a question mark, permanently. The "CSV UTF-8" option keeps every character and starts the file with the three bytes EF BB BF. The mark lets Excel recognize its own output, and it breaks a naive reader by attaching an invisible character to the first column name (Excel CSV UTF-8 versus CSV comma delimited compares the formats, and the UTF-8 BOM in CSV files says how to handle it). Read Excel exports with utf-8-sig in Python and with the encoding option in other tools, and check the first header name for an invisible prefix.

The sep= line and other instructions

A first line reading sep=; tells Excel which delimiter to use for that file, whatever the region. Excel hides it. Every other program reads it as the first record, which makes it your header and pushes the real header into the data. If a file you receive starts with it, remove it before parsing, and treat its value as the delimiter. If a file you send is meant for pipelines, do not add it. A related Excel quirk is worth knowing: a file whose first two characters are the capital letters ID can be mistaken for a SYLK file, an old spreadsheet interchange format, and Excel then refuses it with an error about the file format. Naming the first column id in lowercase, or quoting it, avoids that.

Values are written as displayed

A spreadsheet cell has a value and a display format, and CSV can carry only text. Excel writes the displayed text for most formatted cells. A date shows as 3/4/2026 in a US region and 04.03.2026 in a German one, a percentage as 50%, a number with a thousands format as 1,234.50 (and then quoted, since it now contains a comma). The stored value is not written, so you cannot recover it, and the display depends on the sender's locale. The same applies in reverse: numbers longer than 15 digits are rounded when read (keeping long numbers intact when Excel opens a CSV), and leading zeros vanish on open.

Formulas are saved as the results of the calculation. A cell with =A1*B1 is written as a number. A text cell that begins with = is a different matter: if it arrives as text in your pipeline and goes back out to a spreadsheet, it becomes a live formula, which is the CSV injection issue (CSV injection and how formulas in a cell become an attack).

Line breaks inside cells

A person who presses Alt+Enter in a cell puts a line feed in it. Excel writes that cell in quotes with a bare LF inside, while the rows end with CRLF. A tool that splits the file on lines before parsing sees extra lines in the middle of a record, which is how a file with 1,000 records reports 1,040 lines. The fix is to parse the file with a CSV reader that understands quoted newlines, and to open files in Python with newline="" so the module sees the original line endings (line endings in CSV files).

A converter to RFC form

For a pipeline that wants one clean dialect, convert at the door. The script below reads an Excel-style file, handles the BOM through utf-8-sig, notices a sep= line and uses its delimiter, converts a decimal comma in the columns you name and writes comma-separated UTF-8 with LF line ends:

excel_to_rfc.pyPython
import csv, re, sys

src, dst, decimal_cols = sys.argv[1], sys.argv[2], set(sys.argv[3:])

with open(src, newline="", encoding="utf-8-sig") as f:
    first = f.readline()
    m = re.match(r"sep=(.)\s*$", first)
    delimiter = m.group(1) if m else ","
    if not m:
        f.seek(0)
    reader = csv.reader(f, delimiter=delimiter)
    header = next(reader)
    rows = [header]
    for row in reader:
        rows.append([
            v.replace(".", "").replace(",", ".") if name in decimal_cols else v
            for name, v in zip(header, row)
        ])

with open(dst, "w", newline="", encoding="utf-8") as f:
    csv.writer(f, lineterminator="\n").writerows(rows)
print(f"{len(rows) - 1} rows, delimiter {delimiter!r}")

The input was a file with a BOM, a sep=; line, CRLF rows, a cell with an embedded line break and a text value starting with =:

Text
2 rows, delimiter ';'
id,name,amount,note
1,Müller GmbH,1234.50,"two
lines"
2,Dupont & Fils,89.90,=SUM(A1)

The BOM and the sep= line are gone, the delimiter is a comma, the decimal comma became a point (the thousands point in 1.234,50 was removed first) and the multi-line cell survived inside quotes. The =SUM(A1) in the last field is still there. A converter that changes the dialect does not make the content safe, so run any untrusted text through the escaping step before it goes back to a spreadsheet (escaping formula characters in a CSV export). A file without a sep= line and with commas, such as a second sample I tried, passed through unchanged apart from the BOM.

When Excel's dialect is the one you want

If the only readers are people with Excel, write Excel's dialect on purpose. Use UTF-8 with a BOM so accents survive the double-click, the delimiter of the audience's region, the decimal separator they expect and dates they can read. Forcing an RFC file with no BOM on a Windows user gives them garbled accents, and you will hear about it. The right framing is that the file has a consumer, and the consumer's dialect is the right one.

The better answer for mixed audiences is to offer two outputs, one for people and one for systems, with the same data and named for their purpose. And when the data matters more than the double-click, send an .xlsx with typed cells and keep the CSV as the machine copy. The RFC is available at rfc-editor.org, and Microsoft publishes the numbers behind the cell and row limits in its Excel specifications and limits page.

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.