Spreadsheet Export Formats Compared

Exporting the same sheet to different formats changes more than the extension: some preserve data types, some degrade everything to strings, some open straight in Excel, some suit machines better. This page compares four common export formats and lists the CSV escaping rules that most often go wrong.

How It Works

Tabular data has several carriers: CSV is plain text, comma-separated, with no type or style — ideal for import/export between programs but lossy; XLSX is OOXML (XML inside a ZIP) with formulas, styles and multiple sheets; ODS is the OpenDocument equivalent.

Interop breaks in three spots: dates parse differently by locale, encoding (BOM/UTF-8) mangles non-ASCII text, and formulas differ in function names/separators across apps. Pinning down delimiter, encoding and decimal symbol before export avoids most "garbled or textified numbers" problems.

When to Use This Table

This table is for choosing a spreadsheet format for data exchange. It compares CSV, TSV, XLSX, ODS, Parquet and Arrow by structure, type preservation, best use and common pitfalls. The choice depends on whether humans or programs read the data.

When not to consult it: you are choosing a database or designing warehouse layers. Spreadsheet formats are for exchange and light storage, not large-scale analytical storage.

Rule of thumb: ask who reads it. For humans opening it in Excel, use CSV or XLSX; for programs needing types and structure, use XLSX (limited) or Parquet/Arrow (lossless and fast). CSV is the most universal exchange format but it is a flat structure with no multi-sheet concept and no types.

Mind encoding and delimiters: CSV has no built-in encoding declaration. Excel on Chinese Windows saves as GBK by default while Mac and Linux save UTF-8, and without a BOM Chinese text turns to mojibake. Delimiters also differ by locale (comma, semicolon, tab). Always state encoding and delimiter explicitly for cross-system exchange rather than relying on defaults.

Boundaries and Common Pitfalls

  • CSV loses types: numbers, dates and booleans all become text

    CSV stores only strings, and Excel guesses types on open: 00123 becomes 123 with the leading zeros lost, 2024-06-01 may be parsed as a date and redisplayed in local format, TRUE becomes boolean, and long integer IDs turn into scientific notation. A single round trip can corrupt data. If types must survive, CSV is not an option — use XLSX, or encode type information in the data itself such as writing dates as 2024-06-01T00:00:00Z and IDs as explicitly declared strings.

  • Encoding and BOM mismatch produces mojibake

    A typical failure: a Chinese Windows system exports GBK without BOM while a Linux reader assumes UTF-8 and produces mojibake; conversely a UTF-8 file without BOM opened by double-clicking in Excel also garbles because Excel detects BOM-less UTF-8 unreliably. The robust answer: export UTF-8 with BOM, which both Excel and modern tooling read correctly; detect the BOM before choosing a decoder; and state encoding and delimiter explicitly in any exchange documentation.

  • Exceeding Excel limits silently truncates or refuses to open

    Excel caps a sheet at 1,048,576 rows by 16,384 columns, and behaviour past that depends on the tool: pandas raises an error, but some export libraries silently write only the first 1,048,575 rows with no indication. Excel cells also hold at most 32,767 characters, so long text such as an embedded JSON blob gets cut. Past these magnitudes change format: CSV has no row limit (though memory becomes the constraint), and Parquet or Arrow are the real answer for large volumes.

How This Relates to Neighbours

To convert formats, use the in-site spreadsheet tools; this page is a selection reference.

CSV parsing and escaping of quotes, newlines and delimiters is an implementation problem governed by RFC 4180.

Excel features such as formulas, macros and charts are entirely unsupported in CSV; use XLSX and learn its limits.

If the goal is debugging mojibake in an exported file, check encoding and BOM first, then delimiters and row limits.

Practical Snippet

Key points: (1) CSV stores only strings and Excel guesses types, so 00123 becomes 123, dates get re-parsed and long integer IDs become scientific notation — use XLSX when types must survive, or encode type in the data with ISO 8601 dates and string IDs; (2) always export CSV as UTF-8 with BOM so double-clicking in Excel does not garble, and detect the BOM before decoding rather than guessing since GBK and UTF-8 look similar; (3) Excel caps at 1,048,576 rows by 16,384 columns and 32,767 characters per cell, and some exporters truncate silently past that without any indication — switch to Parquet or Arrow at larger volumes; (4) escape CSV fields per RFC 4180, doubling quotes and wrapping when the value contains delimiters, quotes or newlines.

// ① CSV 没有类型信息 ⇒ 需要类型时用 XLSX,或把类型写进数据
// ❌ 直接写:00123 会被 Excel 读成 123
// ✅ 明确声明
const rows = [
  { id: '00123', createdAt: '2024-06-01T00:00:00Z', amount: '19.99', active: 'true' },
]
// 日期用 ISO 8601 带时区、ID 用字符串、金额用字符串保精度
// 导出为 CSV 时至少加表头说明类型,或直接改用 XLSX

// ② 导出 CSV 一律用 UTF-8 with BOM(Excel 双击才不会乱码)
// Node 侧手动加 BOM
function toCsvWithBom(rows) {
  const body = rows.map(r => Object.values(r).map(csvEscape).join(',')).join('
')
  return '' + body                    // BOM 前置
}
function csvEscape(v) {
  const s = String(v ?? '')
  // RFC 4180:含分隔符/引号/换行时用双引号包裹,内部引号翻倍
  return /[",
]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s
}

// ③ 读取 CSV:先探测 BOM,再按声明的编码解码
function decodeCsv(buffer, declaredEncoding = 'utf-8') {
  const b = new Uint8Array(buffer)
  if (b[0] === 0xef && b[1] === 0xbb && b[2] === 0xbf) {
    return { encoding: 'utf-8-bom', text: new TextDecoder('utf-8').decode(b.slice(3)) }
  }
  if (b[0] === 0xff && b[1] === 0xfe) {
    return { encoding: 'utf-16le', text: new TextDecoder('utf-16le').decode(b) }
  }
  // 无 BOM ⇒ 用声明的编码(不要靠猜,中文场景 GBK 与 UTF-8 视觉接近)
  return { encoding: declaredEncoding, text: new TextDecoder(declaredEncoding).decode(b) }
}

// ④ Excel 硬限制:超限时**静默截断**是最大的坑
const XLSX_LIMITS = { rows: 1048576, cols: 16384, cellChars: 32767 }
function checkExcelLimits(data) {
  const warnings = []
  if (data.length > XLSX_LIMITS.rows - 1) {
    warnings.push('行数 ' + data.length + ' 接近上限,导出器可能静默截断')
  }
  for (const row of data) {
    for (const [k, v] of Object.entries(row)) {
      if (String(v ?? '').length > XLSX_LIMITS.cellChars) {
        warnings.push(k + ' 超单元格字符上限会被截断')
        break
      }
    }
  }
  return warnings        // 空数组才安全;非空请改用 Parquet/Arrow
}

Practical Notes

  • CSV has no standard; the comma is only a conventionThe delimiter may be a comma, semicolon or tab, the encoding may be UTF-8 or GBK, and quoting rules differ per producer. Exchanging data between systems should prefer a schema-carrying format, or at minimum agree on delimiter, quoting and encoding.
  • Excel turns long numbers in CSV into scientific notationAn 18-digit order number exported as digits is parsed as a float and loses its last digits (shown as 1.23E+17). Export that column as text with a prefix (="123…" or a leading apostrophe), or ship xlsx instead.
  • Newlines inside CSV cells must be quotedFields containing a newline or the delimiter need double quotes, and inner quotes must be doubled (""). Skip this and any line-based consumer splits one record into two — usually discovered only when the totals do not reconcile.

Data Sources

  • RFC 4180 (CSV)
  • ECMA-376 (Office Open XML)
  • OASIS Open Document Format

Codes, names and definitions above come from public specifications; we claim no originality over the specification data itself. The arrangement, Chinese annotations and practical notes on this page are written by us.

Four Export Formats

FormatStructureTypesBest ForWatch Out
JSONArray of objects keyed by headerPreserves numbers, booleans and nullAPI wiring, front-end rendering, config importDuplicate headers overwrite; merged cells lose structure
CSVPlain text table, comma separatedEverything degrades to stringsDatabase import, opening in Excel, cross-platform exchangeNo types and no encoding declaration; CJK often mojibakes
HTMLtable / tr / td markupPreserves text and merged structureEmbedding in pages, email bodies, rich-text pasteLarge output; numbers remain text
Plain textTab or pipe delimitedEverything is a stringLogs, terminal paste, Markdown tablesFields containing the delimiter must be escaped or columns shift

CSV Escaping Rules

Whenever a field contains a comma, newline or double quote it must be wrapped in double quotes, and inner quotes are written as two consecutive quotes.

CaseInputWritten in CSVNote
Plain fieldappleappleEmitted as is
Contains a commaa,b"a,b"Must be wrapped in double quotes
Contains a newlineline1 line2"line1 line2"Newlines are allowed inside quotes
Contains a double quotesay "hi""say ""hi"""Inner quotes are doubled
Leading/trailing spaces pad " pad "Quote it to preserve the spaces

Frequently Asked Questions

Should CSV use commas or tabs as the delimiter?

It depends on the data. Commas are the RFC 4180 standard with the best compatibility, but if the data itself contains commas — common in addresses, amounts and person names — fields must be wrapped in double quotes, and getting that wrong shifts every column. Tab-separated TSV is far less likely to appear inside the text, which suits comma-heavy data, at the cost of weaker default support. A safe rule: prefer commas with strict quote escaping; if you control the downstream parser, TSV removes a great deal of escaping pain.

Why does Chinese text mojibake when opening a CSV in Excel?

CSV declares no encoding, so Excel guesses using the system default. Chinese Windows defaults to GBK, while most exporters write UTF-8, and the mismatch produces mojibake. Three fixes: write a UTF-8 BOM, which tells Excel to parse as UTF-8 and is the least effort; have the recipient use Data → From Text and pick UTF-8 explicitly; or save as GBK, which sacrifices compatibility elsewhere.

Why did my dates turn into numbers after export?

This is how Excel stores dates internally: a date is just a serial number counted from 1900-01-01, and the displayed form is entirely a matter of cell formatting. Exporting to CSV drops the formatting, leaving only the serial. Format the date column as text before exporting, or convert it with the TEXT function into a string like yyyy-mm-dd. When exporting JSON, the tool should recognise date formats and emit ISO 8601 strings rather than the raw internal number.

Exporting a large sheet is slow or fails. What should I do?

The bottleneck is usually browser memory rather than the computation. A 100,000-row sheet parsed into an array of objects can occupy several times the original file size, and string concatenation pushes it over the edge. Practical steps: trim columns and rows to what the business actually needs instead of exporting everything; move sheets beyond a few tens of thousands of rows to a server-side script; and if the consumer is another program, choose CSV over JSON or HTML, since it is an order of magnitude lighter in both memory and bytes.