The three targets
| Target | Bytes | Expected by |
|---|---|---|
| LF | 0A | Unix, macOS, the web, JSON, Docker, most build tools |
| CRLF | 0D 0A | Windows editors, HTTP/1.1 headers (RFC 9112), CSV records (RFC 4180) |
| CR | 0D | Classic Mac OS through version 9, and some serial devices |
Every input, every target, byte for byte
7 inputs — one for each of the 6 break forms the tool recognises, plus a file that mixes three of them. Each converted to each of the three targets, with the UTF-8 bytes before and after. This table is generated by calling the tool while the page is built, so it cannot describe a conversion the tool does not perform, and the single-form rows are derived from the splitter's own list of break kinds, so it cannot leave one out.
| Input | Bytes in | → LF | → CRLF | → CR |
|---|---|---|---|---|
| LF input alpha␊beta The common case: a file written on Unix or by a code editor. | 61 6C 70 68 61 0A 62 65 74 61 10 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (+0) · unchanged | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (+1) | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (+0) |
| CRLF input alpha␍␊beta A file written on Windows. | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (-1) | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (+0) · unchanged | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (-1) |
| CR input alpha␍beta Classic Mac OS, or a CRLF that lost its LF. | 61 6C 70 68 61 0D 62 65 74 61 10 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (+0) | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (+1) | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (+0) · unchanged |
| NEL input alpha⟦NEL⟧beta A mainframe export transcoded from EBCDIC. | 61 6C 70 68 61 C2 85 62 65 74 61 11 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (-1) | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (+0) | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (-1) |
| LS input alpha⟦LS⟧beta A soft break pasted out of a layout application. | 61 6C 70 68 61 E2 80 A8 62 65 74 61 12 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (-2) | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (-1) | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (-2) |
| PS input alpha⟦PS⟧beta A paragraph boundary pasted out of a layout application. | 61 6C 70 68 61 E2 80 A9 62 65 74 61 12 bytes | 61 6C 70 68 61 0A 62 65 74 61 10 bytes (-2) | 61 6C 70 68 61 0D 0A 62 65 74 61 11 bytes (-1) | 61 6C 70 68 61 0D 62 65 74 61 10 bytes (-2) |
| Mixed file alpha␍␊beta␊gamma␍delta Three different endings in four lines — the state a file reaches after two people edit it on two platforms. | 61 6C 70 68 61 0D 0A 62 65 74 61 0A 67 61 6D 6D 61 0D 64 65 6C 74 61 23 bytes | 61 6C 70 68 61 0A 62 65 74 61 0A 67 61 6D 6D 61 0A 64 65 6C 74 61 22 bytes (-1) | 61 6C 70 68 61 0D 0A 62 65 74 61 0D 0A 67 61 6D 6D 61 0D 0A 64 65 6C 74 61 25 bytes (+2) | 61 6C 70 68 61 0D 62 65 74 61 0D 67 61 6D 6D 61 0D 64 65 6C 74 61 22 bytes (-1) |
The LS and PS rows are the ones to read twice. Each starts 2 bytes larger than the LF row, because U+2028 and U+2029 are three UTF-8 bytes each — E2 80 A8 and E2 80 A9 — where LF is one. Converting either to LF therefore shrinks the file by 2 bytes even though the number of lines is unchanged. Nobody expects an ending conversion to change the size by more than one byte per line, and this is why it can.
The mixed row is the one you will actually meet. Four lines separated by CRLF, LF
and CR in that order — 61 6C 70 68 61 0D 0A 62 65 74 61 0A 67 61 6D 6D 61 0D 64 65 6C 74 61 — become uniform under
every target. That is the operation you actually want after two people have edited a file on
two platforms, and it is one that a search-and-replace cannot do in a single pass without
creating \r\r\n somewhere along the way.
The round-trip check
Converting to CRLF and back to LF must give the same answer as converting straight to LF.
Every sample above was run through both routes and compared:
true. That property is not free — a converter that
turned each LF into CRLF with a plain replacement would turn a CRLF file into
\r\r\n per line, and the second conversion would then have real damage to work
with. This tool splits on break kinds and re-emits them, so a CRLF is handled as
one token and never as two characters that happen to be adjacent.
Reversibility stops at normalisation. Converting a mixed file to LF discards the information
about which breaks were CRLF, and no later conversion can recover it. If you need the
original, keep it — this is exactly the hazard git's own documentation flags under
core.safecrlf, quoted on the CRLF vs LF page.
What CRLF costs
Multiplication, not measurement: 1 extra byte per line — computed from the table above, not assumed — times the number of lines. For a document that is nothing. For a 1,000,000-line log it is 1,000,000 bytes, about 977 KiB: real, and still the least interesting reason to prefer LF. Compatibility with the tools that have to read the file is the reason that matters.
Doing it from the command line
Worth knowing, because the browser round trip is awkward for a file you are already working with in a terminal. Every one of these does the same job as the tool above for the LF target:
# Git Bash / WSL — GNU tr and GNU sed
tr -d '\r' < input.txt > output.txt # deletes every CR, including deliberate ones
sed -i 's/\r$//' input.txt # deletes only a CR at end of line
# PowerShell 7
(Get-Content -Raw input.txt) -replace "`r`n", "`n" | Set-Content -NoNewline output.txt
Note the difference between the first two. tr -d '\r' removes every carriage
return in the file, which is fine for text and wrong for anything binary that slipped in.
The sed form only removes one at end of line, which is what you almost always
mean. Neither handles NEL or U+2028, which is the gap the tool above exists to fill — the
full comparison of which mechanisms recognise which
break characters is on the word-processor page.
Which sed, though. The two shell lines are the GNU ones Git
Bash ships, and this page was built on Windows with nothing else to try them against. In
particular -i is the flag whose spelling is worth checking before you paste
that line into a shell somewhere else: run sed --help there first. Nothing on
this site is a claim about a system it was not built on.
Checking your work
Do not trust the appearance of the result. Press Show invisible characters and read the badges: a uniform column of LF or CRLF means the conversion took, and a stray CR means it did not. The counter strip under the input does the same job in one line, which is the faster check on a long file.