The rule that makes this solvable
RFC 4180 is the closest thing CSV has to a specification, and two of its rules are all you need. Rule 1: “Each record is located on a separate line, delimited by a line break (CRLF).” Rule 6: “Fields containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes.” Both quoted from the RFC on rfc-editor.org, read directly.
Put together, they say that a line break is a record separator unless it sits between a pair of double quotes — in which case it is data. So the file is not ambiguous at all; it is only ambiguous to a program that splits on line breaks without tracking quotes. That program is the one every "convert CSV" snippet contains.
What a naive split gets wrong
Seven CSV files, each written the way the RFC specifies: CRLF between records, and — where
a cell has a manual break — a break inside the quotes. Column two is how many records
text.split(/\r?\n/) believes there are. Column three is how many there
actually are.
| File | Naive records | RFC 4180 records | With CSV mode on | With CSV mode off |
|---|---|---|---|---|
| id,name␍␊1,Ada␍␊2,Grace Three ordinary records, nothing quoted. | 3 | 3 | id,name␍␊1,Ada␍␊2,Grace 3 records kept | id,name·1,Ada·2,Grace 1 record left |
| id,address␍␊1,"12·Long·Road␊Springfield"␍␊2,"Elsewhere" One cell holds a manual line break. | 4 wrong | 3 | id,address␍␊1,"12·Long·Road·Springfield"␍␊2,"Elsewhere" 3 records kept | id,address·1,"12·Long·Road·Springfield"·2,"Elsewhere" 1 record left |
| id,note␍␊1,"first␊second␊third" Two breaks inside a single cell. | 4 wrong | 2 | id,note␍␊1,"first·second·third" 2 records kept | id,note·1,"first·second·third" 1 record left |
| id,quote␍␊1,"she·said·""hello""␊and·left" A quoted cell that also contains a doubled quote. | 3 wrong | 2 | id,quote␍␊1,"she·said·""hello""·and·left" 2 records kept | id,quote·1,"she·said·""hello""·and·left" 1 record left |
| id,name␍␊1,"Lovelace,·Ada␊Countess" A quoted cell containing a comma and a break. | 3 wrong | 2 | id,name␍␊1,"Lovelace,·Ada·Countess" 2 records kept | id,name·1,"Lovelace,·Ada·Countess" 1 record left |
| id,note␍␊1,"line·one␍␊line·two" The break inside the cell is itself CRLF, as RFC 4180 writes it. | 3 wrong | 2 | id,note␍␊1,"line·one·line·two" 2 records kept | id,note·1,"line·one·line·two" 1 record left |
| id,name␍␊1,Ada␍␊ A file that ends with a record separator. | 3 | 3 | id,name␍␊1,Ada␍␊ 3 records kept | id,name·1,Ada 1 record left |
The naive count is wrong on 5 of the 7 files — every one that has a break inside a cell, which is every file anybody has this problem with. CSV mode preserved the record count on all 7 of them; the same transformation with CSV mode off collapsed 7 of 7 into a single record, because without the quoting rule there is nothing to tell it to stop.
Note the fourth file in particular. It contains ""hello"" — a doubled quote,
which RFC 4180 defines as one literal quote character inside the field, not as the field
ending and another starting. A quote-tracking splitter that misses that escape rule flips
its idea of "inside" and "outside" for the whole rest of the file, and the damage is silent.
What we did not test
Excel itself. We had no copy of Excel to test against, so this page makes no claim about which character code Excel writes for Alt+Enter, what its CSV export does with a cell that already contains a CRLF, or how it round-trips a file. The only Microsoft documentation this page cites is the CHAR function page, which notes that Excel for the web supports CHAR(9), CHAR(10), CHAR(13) and CHAR(32) and above — and it gives no line-break example at all.
That is not a gap you have to accept, because you have a measuring instrument: paste one of your own cells above and press Show invisible characters. The badge names exactly which character your build of your spreadsheet put there, on your machine, today. A table of what some other version did on some other machine would be worth less.
Doing it without leaving the spreadsheet
If you would rather not round-trip through a text tool, the in-sheet approach is a SUBSTITUTE against the character code. Find out which code you have first — the reveal view above will tell you — and then substitute that one:
=SUBSTITUTE(A1, CHAR(10), " ") the LF case
=SUBSTITUTE(A1, CHAR(13), " ") the CR case
=TRIM(SUBSTITUTE(SUBSTITUTE(A1, CHAR(13), " "), CHAR(10), " ")) both, tidied The nested form matters for the same reason the whole site exists: if the cell holds CR + LF and you substitute only one of them, the other stays, invisible, and every comparison against that cell quietly fails. TRIM on the outside collapses the doubled spaces the substitution leaves.
The find-and-replace dialog is the other route. Its search box does not accept a typed line break, which is the reason the question gets asked at all; the substitution formulas above sidestep the dialog entirely.
Going the other way
Two related jobs come up constantly and both live elsewhere on this site. To turn a column of cells into one delimited string — for a formula, a query, or a mail merge — use join lines, which can also wrap each value in SQL or JSON quoting. To fix the line endings of a file a spreadsheet wrote so that a script can read it, use convert line endings.
A short checklist
- Save or copy as CSV, not as a formatted paste — formatted pastes lose the quoting that makes this solvable.
- Paste, and read the badge counts. CRLF between records and LF inside cells is the normal picture.
- Leave CSV mode on. It is the only reason your rows survive.
- Check the result's record count against the original before pasting it back.