Formats¶
Which kind of file it is comes from its bytes, never its name. What each kind reads as:
An OpenDocument cell says its own type¶
A workbook guesses dates from number formats; a .ods cell states its type beside its value, so
there is nothing to guess. float, percentage and currency read as numbers, date as a date,
time as the workbook serial of as many days — a time of day on 31 December 1899, the day an xlsx
time-only cell reads on, and a longer duration on the day that serial names — and boolean as a
boolean. Anything else is text: the cell's office:string-value when it has one, else its paragraphs
joined by a line feed, comments left out. A formula reads as the value the writer cached. ODF has no
error type; LibreOffice marks a failed formula in an extension attribute, and it reads as an error
carrying the text the cell shows — #N/A, #REF!, or LibreOffice's own Err:502.
A row or cell repeated by attribute is expanded only when it holds a value; the million empty rows LibreOffice declares after the last one cost nothing. Covered cells of a merge read as empty. Hidden sheets and rows read like any other, as in xlsx. A sheet is a table of the spreadsheet itself: a sub-table inside a cell, or the table a DDE link caches, is not one, and its text is not the cell's.
A zip archive reads as one workbook¶
A zip that is not itself a workbook is read as one: its sheets are the sheets of every file in it
that can be read as a table — csv, xlsx and ods — in the order of their paths, each with its file's
path as SheetInfo.Source and its own Format. A csv file is named after its file; a workbook's
sheets keep their names, and Source tells two Sheet1 apart. Files are judged by their bytes, as a
file on its own is.
- Left out without a word: directories, hidden files and folders (
.DS_Store, anything whose name starts with a dot) and__MACOSX/. - Skipped with a reason in
FileProfile.SkippedEntries: an encrypted file, a nested zip, another OpenDocument type, a legacy.xls, an XML document, a binary file, and a workbook that is damaged or of a kind not read (.xlsb). Nested archives are not opened. - Refused: an archive with nothing readable in it, as
format.unsupported.
A csv file is read as a stream straight out of the archive, never unpacked, with its dialect
decided from its own head; moving back to it reads it again from its first row. A workbook has to be
read with random access, so it is copied into memory while its sheets are read — one at a time, up to
ArchiveCursorOptions.MaxEmbeddedWorkbookBytes (256 MB by default, and any size a server can
afford: the copy is held in pieces, not one array). A mapping plan made from an archive records the
sheet's Source, and an import refuses the archive as structure.sheet-changed when another file now
stands at the plan's index.
Malformed input is repaired, and the repair is counted¶
Files that people upload are not well-formed. A 572 MB real-world export carries quotes inside
unquoted fields (100 21"st AVE), text after a closing quote ("C" Road), and 302 quotes that
are never closed. Refusing such a file is defensible and useless; one bad address must not fail an
import of five million rows.
So the reader recovers — and counts what it recovered in CursorDiagnostics. A silent recovery is
indistinguishable from correct reading, and that is the defect: left unbounded, those 302 quotes
swallow about 39,000 records without a word.
Two repairs, both counted. A quoted field that has crossed a line ending and holds a whole record's
worth of delimiters was never a quote — a lone " that opened a field and swallowed the records
after it — and those records are read as records the moment that is clear (RecoveredStrayQuotes).
Past a line ending a quote also closes a field only where a field can end, so an inch mark in the
swallowed text (135"th) does not close it. Behind that, a quoted field longer than 100 lines, or
one the file ends inside, is abandoned the same way (RecoveredUnterminatedQuotes); that bound is
what protects tables narrower than five columns, where the delimiter test is off. Genuine multi-line
values — an address, a long note — hold a delimiter or two at most and are left alone.