{
  "version": "https://jsonfeed.org/version/1",
  "title": "Excel on LLBBL Blog",
  "icon": "https://avatars.micro.blog/avatars/2023/40/125738.jpg",
  "home_page_url": "https://llbbl.blog/",
  "feed_url": "https://llbbl.blog/feed.json",
  "items": [
      {
        "id": "http://llbbl.micro.blog/2026/08/19/nobody-agrees-what-a-csv.html",
        "title": "Nobody Agrees What a CSV Is",
        "content_html": "<p>CSV is simple but powerful. Values, separated by commas. It&rsquo;s easy to understand and use.</p>\n<p>It is also, by a wide margin, the one that destroys the most data.</p>\n<p>That&rsquo;s not a paradox. It&rsquo;s cause and effect. A format simple enough that everyone writes their own parser is a format with as many dialects as it has parsers, and CSV&rsquo;s defining property is that it carries no information about how to read it.</p>\n<hr>\n<h2 id=\"there-is-no-standard\">There Is No Standard</h2>\n<p>RFC 4180 exists. Yakov Shafranovich published it in October 2005, it registers the <code>text/csv</code> media type, and it gives an ABNF grammar.</p>\n<p>It is also <strong>Informational</strong>, not Standards Track. It was written to describe what people were already doing, twenty-odd years after spreadsheets started emitting it. Section 2 says so outright: there is &ldquo;no formal specification in existence,&rdquo; and what follows documents &ldquo;the format that seems to be followed by most implementations.&rdquo;</p>\n<p>By the time someone wrote it down, every spreadsheet, database, and scripting language had already shipped its own interpretation. The RFC didn&rsquo;t settle anything. It just added one more dialect, with the distinction of having a number.</p>\n<p>Eight years later, RFC 7111 added URI fragments for pointing at a row, column, or cell inside a <code>text/csv</code> file. Also Informational. CSV still had no standard, but you could now cite a specific cell of one.</p>\n<hr>\n<h2 id=\"where-does-a-record-end\">Where Does a Record End?</h2>\n<p>The obvious answer is &ldquo;at the newline,&rdquo; and the obvious answer is wrong, because a quoted field is allowed to contain one.</p>\n<pre tabindex=\"0\"><code>name,notes\nAlice,&#34;line one\nline two&#34;\nBob,fine\n</code></pre><p>That&rsquo;s a valid three-row CSV. Split it on newlines and you get four:</p>\n<pre tabindex=\"0\"><code>naive split gives 4 lines:\n   &#39;name,notes&#39;\n   &#39;Alice,&#34;line one&#39;\n   &#39;line two&#34;&#39;\n   &#39;Bob,fine&#39;\n\na real CSV parser gives 3 rows:\n   [&#39;name&#39;, &#39;notes&#39;]\n   [&#39;Alice&#39;, &#39;line one\\nline two&#39;]\n   [&#39;Bob&#39;, &#39;fine&#39;]\n</code></pre><p>Every <code>head</code>, <code>wc -l</code>, <code>split(&quot;\\n&quot;)</code>, and shell pipeline that assumes one record per line is wrong on this file. Not wrong on a malformed file. Wrong on a correct one.</p>\n<p>This is the single most common CSV bug, and it&rsquo;s invisible in testing, because your test fixtures don&rsquo;t have newlines in them until a user pastes an address into a form.</p>\n<hr>\n<h2 id=\"whats-the-delimiter\">What&rsquo;s the Delimiter?</h2>\n<p>In most of Europe the decimal separator is a comma. <code>12,50</code> is twelve and a half euros. Which means a comma cannot also be a field separator, so those locales use semicolons.</p>\n<p>Feed a German CSV to an RFC 4180 parser and everything survives, in the sense that nothing throws:</p>\n<pre tabindex=\"0\"><code>input:  produkt;preis\n        Kaffee;12,50\n        Tee;9,90\n\nparsed as comma-delimited:\n   [&#39;produkt;preis&#39;]\n   [&#39;Kaffee;12&#39;, &#39;50&#39;]\n   [&#39;Tee;9&#39;, &#39;90&#39;]\n\nparsed as semicolon-delimited:\n   [&#39;produkt&#39;, &#39;preis&#39;]\n   [&#39;Kaffee&#39;, &#39;12,50&#39;]\n   [&#39;Tee&#39;, &#39;9,90&#39;]\n</code></pre><p>The first reading gives you two columns of nonsense with no error. The prices split down the middle of the decimal point. A pipeline that ingests this will happily compute statistics on the number 12 and the number 50.</p>\n<p>Excel picks the delimiter based on your operating system&rsquo;s regional settings, which means the same file opens differently on two machines in the same office.</p>\n<hr>\n<h2 id=\"is-the-first-row-a-header\">Is the First Row a Header?</h2>\n<pre tabindex=\"0\"><code>1,2,3\n4,5,6\n</code></pre><p>Header or data? Nothing in the file says.</p>\n<p>RFC 4180&rsquo;s answer is that you put it in the MIME type: <code>text/csv; header=present</code>. Which is a real answer, and also means the information lives outside the file, in a transport layer that gets stripped the moment someone saves the attachment to disk.</p>\n<p>So in practice every tool guesses, usually by checking whether the first row looks less numeric than the rest.</p>\n<hr>\n<h2 id=\"the-part-that-destroys-data\">The Part That Destroys Data</h2>\n<p>Everything above is a parsing problem. This one is worse, because the file parses fine and the damage happens after.</p>\n<p>CSV has no types. Every value is text. So every spreadsheet and dataframe library applies type inference on import, and type inference is lossy.</p>\n<p>Here&rsquo;s a file with four columns of identifiers, all of which are strings that happen to be made of digits:</p>\n<pre tabindex=\"0\"><code>gene,zip,card,accession\nSEPT7,02138,4532012345678901,0004928\nMARCH1,01234,4111111111111111,0000071\n</code></pre><p>Read it with a type-inferring reader and:</p>\n<pre tabindex=\"0\"><code>  gene  zip             card  accession\n SEPT7 2138 4532012345678901       4928\nMARCH1 1234 4111111111111111         71\n</code></pre><p>The ZIP code <code>02138</code> is now <code>2138</code>. The accession number <code>0004928</code> is now <code>4928</code>. Nobody was asked. Nothing warned. Save that back to CSV and the original values are gone from disk.</p>\n<p>Spreadsheets are worse than this, because they store every number as an IEEE 754 double. Microsoft is blunt about the consequence:</p>\n<blockquote>\n<p>Excel has a maximum precision of <strong>15 significant digits</strong>, which means that for any number containing 16 or more digits, such as a credit card number, any numbers past the 15th digit are rounded down to zero.</p>\n</blockquote>\n<p>The example they reach for is a card number:</p>\n<pre tabindex=\"0\"><code>typed into a cell     1234 5678 9087 6543\nExcel shows           1.23E+15\n</code></pre><p>Microsoft calls that &ldquo;truncating numerical data to 15 digits of precision and converting to a number displayed in scientific notation.&rdquo; Note that this is the vendor describing its own product, not a bug report.</p>\n<p>The card number in the file above is also 16 digits. pandas read it back intact. Excel would not.</p>\n<p>Credit card numbers are 16 digits. Many national ID numbers are longer. They are not numbers in any meaningful sense, they&rsquo;re strings of digits, and a format with no type information cannot tell the difference.</p>\n<hr>\n<h2 id=\"the-gene-name-problem\">The Gene Name Problem</h2>\n<p>The best-documented case of this is genomics, because biologists name genes things like <code>SEPT1</code> and <code>MARCH1</code> and spreadsheets read those as dates.</p>\n<p>In 2016 Ziemann and colleagues screened 35,175 supplementary Excel files from 18 journals covering 2005 to 2015. Among articles containing Excel gene lists, <strong>19.6%</strong> had gene names corrupted this way. One in five.</p>\n<p>A follow-up in 2021, &ldquo;Gene name errors: Lessons not learned,&rdquo; found <strong>30.9%</strong> across a broader sample drawn from PubMed Central. Worth being careful comparing those two numbers directly, because the second study used a different sampling frame and also detected an additional error category the first one didn&rsquo;t look for. The honest summary is that the problem did not go away in the five years after being loudly published.</p>\n<p>The resolution is the remarkable part. The field did not fix the spreadsheets. It <strong>renamed the genes</strong>. The HUGO Gene Nomenclature Committee&rsquo;s 2020 guidelines state that &ldquo;all symbols that auto-converted to dates in Microsoft Excel have been changed,&rdquo; giving <code>SEPT1</code> becoming <code>SEPTIN1</code> and <code>MARCH1</code> becoming <code>MARCHF1</code> as examples.</p>\n<p>Human genes were renamed because a file format cannot say what type a column is.</p>\n<hr>\n<h2 id=\"what-to-do-about-it\">What To Do About It</h2>\n<p>CSV isn&rsquo;t going away, and mostly shouldn&rsquo;t. It&rsquo;s readable, streamable, diffable, and every tool on earth reads it.</p>\n<p>The practical defenses are short:</p>\n<ul>\n<li><strong>Quote everything.</strong> It&rsquo;s never wrong and it removes a whole class of ambiguity.</li>\n<li><strong>Treat identifiers as strings explicitly</strong> at the point of import. Every serious CSV reader lets you pin column types; use it.</li>\n<li><strong>Never round-trip through a spreadsheet</strong> if the data contains identifiers. Opening and saving is a lossy operation.</li>\n<li><strong>Say what you mean out of band.</strong> Delimiter, encoding, header presence, quoting style. The file will not.</li>\n<li><strong>Use something else when you can.</strong> Parquet and even JSON Lines carry types. If the consumer is a program rather than a person, the readability argument for CSV mostly evaporates.</li>\n</ul>\n<p>The lesson generalizes past CSV, and it&rsquo;s the same one from the text file post. A format that carries no description of itself pushes that burden onto every reader, and readers guess. Usually well. Occasionally by silently deleting the leading zero from your ZIP code.</p>\n<h2 id=\"sources\">Sources</h2>\n<ul>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc4180\">RFC 4180</a> — the Informational spec that documents CSV rather than defining it</li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc7111\">RFC 7111</a> — URI fragment selectors for <code>text/csv</code>, January 2014</li>\n<li><a href=\"https://doi.org/10.1186/s13059-016-1044-7\">Ziemann et al., &ldquo;Gene name errors are widespread in the scientific literature&rdquo;</a> — Genome Biology, 2016; the 19.6% figure (<a href=\"https://pmc.ncbi.nlm.nih.gov/articles/PMC4994289/\">free full text</a>)</li>\n<li><a href=\"https://doi.org/10.1371/journal.pcbi.1008984\">Abeysooriya et al., &ldquo;Gene name errors: Lessons not learned&rdquo;</a> — PLOS Computational Biology, 2021; the 30.9% follow-up</li>\n<li><a href=\"https://doi.org/10.1038/s41588-020-0669-3\">Bruford et al., &ldquo;Guidelines for human gene nomenclature&rdquo;</a> — Nature Genetics, 2020; the renaming (<a href=\"https://pmc.ncbi.nlm.nih.gov/articles/PMC7494048/\">free full text</a>)</li>\n<li><a href=\"https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result\">Microsoft on Excel&rsquo;s floating-point precision</a> — Excel follows IEEE 754 and stores 15 digits of precision</li>\n<li><a href=\"https://support.microsoft.com/en-US/Excel/keeping-leading-zeros-and-large-numbers\">Microsoft on leading zeros and large numbers</a> — &ldquo;any numbers past the 15th digit are rounded down to zero,&rdquo; with a credit card as the example</li>\n<li><a href=\"https://support.microsoft.com/en-US/Excel/get-started/import-or-export-text-txt-or-csv-files\">Microsoft on importing and exporting text files</a> — the CSV list separator comes from Windows Region settings</li>\n</ul>\n<blockquote>\n<p>I&rsquo;d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at <a href=\"https://micro.blog/llbbl?remote_follow=1\">@logan@llbbl.blog</a>.</p>\n</blockquote>\n",
        "date_published": "2026-08-19T10:00:00-05:00",
        "url": "https://llbbl.blog/2026/08/19/nobody-agrees-what-a-csv.html",
        "tags": ["Programming","Data","File-formats","Csv","Excel"]
      }
  ]
}
