Fix CSV Encoding

Mojibake is what you get when UTF-8 text is read as Windows-1252 and saved again as UTF-8. This page runs that mistake backwards: it maps each character back to the byte it came from and decodes the result as UTF-8, keeping the repair only when it verifies. It also strips the byte order mark and settles the line endings. Nothing is uploaded.

Only the padding wrong rather than the characters? The whitespace cleaner is next door.

What actually happened to your file

The symptom is unmistakable once you have seen it twice. Every accented character has become two characters, the first of which is almost always à or Â, and every curly quote has become three starting with â.

café      became   café
naïve     became   naïve
Zürich    became   Zürich
José      became   José

Here is the mechanism. In UTF-8, é is two bytes: 0xC3 0xA9. A program that reads those bytes while believing the file is Windows-1252 sees two separate characters, because in that encoding every byte is one character: 0xC3 is à and 0xA9 is ©. It now holds the string café, which it saves as UTF-8, and the damage is baked into the bytes. Nothing later in the pipeline can tell that this was ever meant to be one letter.

The name for this is mojibake, and it is one of the few data problems with a genuinely correct fix rather than a heuristic one. The information is not lost, only re-encoded, so the mistake can be run backwards exactly.

Worked example: a broken export

This is what the example button loads:

id,name,city,note
1,Café Müller,Zürich,"“best espressoâ€[U+009D]"
2,José García,Málaga,visited in May
3,Björn Östberg,Malmö,"– pending –"
4,Ada Lovelace,London,fine already

Drop it in and, with no settings touched:

id,name,city,note
1,Café Müller,Zürich,“best espresso”
2,José García,Málaga,visited in May
3,Björn Östberg,Malmö,– pending –
4,Ada Lovelace,London,fine already

4 rows · no BOM · LF line endings in the source · written with LF
· 8 cells repaired

Eight cells, and the note column is the half that matters. The closing curly quote breaks into three characters and the third of them, written above as [U+009D], is an invisible control character with no printable form at all. The accented names would come back from any repair tool; the curly quotes and the en dashes in the notes are the CP1252 block described below, and a repair built on a plain character-code map loses exactly those. Row 4 is the control. It was never broken, so the detection never looks at it and it comes through untouched. That matters more than it sounds: a repair pass that runs over clean text can break it, and the reason the automatic mode only touches cells carrying the tell-tale pairs is to make that impossible.

The step most repairs get wrong

The obvious implementation is three lines: take each character, if its code is 255 or less write it as a byte, then decode the bytes as UTF-8. That works for the accented letters and loses exactly the characters people most often need repaired.

The reason is that Windows-1252 is not Latin-1, although almost everyone uses the names interchangeably. They agree on bytes 0xA0 to 0xFF. They disagree completely about 0x80 to 0x9F, which Latin-1 defines as control codes and CP1252 fills with printable characters: the curly quotes, the en and em dashes, the ellipsis, the euro sign, the trademark symbol, the bullet. Those are precisely the characters a word processor produces, so they are precisely the characters that end up in a mangled export.

byte 0x80  €    byte 0x91  ‘    byte 0x96  –
byte 0x85  …    byte 0x92  ’    byte 0x97  em dash
byte 0x89  ‰    byte 0x93  “    byte 0x99  ™
byte 0x8C  Œ    byte 0x94  ”    byte 0x9C  œ

A character-code map has no idea that , whose code point is 8217, should become the single byte 0x92. It gives up, the repair fails, and a smart apostrophe stays broken while the accents around it come out fine. The reverse table for that block is built in here, so the punctuation repairs along with everything else.

A repair that cannot be verified is not applied

Running the mistake backwards produces a sequence of bytes. Those bytes are either valid UTF-8 or they are not, and that is a question with a definite answer rather than a judgement call.

The decode is done with TextDecoder("utf-8", { fatal: true }). Without the fatal flag, a decoder given invalid bytes silently substitutes U+FFFD, the replacement character, and hands back a string full of question-mark diamonds that looks like a successful repair. With it, the decode throws, the cell keeps its original text, and the count of cells that looked broken but did not repair cleanly appears in the report.

Two more guards sit alongside it. A repair whose output is identical to its input is not a repair and is not counted as one. And a repair that produces a replacement character anywhere in the result is rejected outright, because that means the bytes were not what the theory said they were.

The practical upshot: the number of repaired cells in the report is a number you can trust. Every one of those cells went through a round trip that came back as valid UTF-8.

The byte order mark, and the line endings

A CSV saved from Excel on Windows usually starts with three invisible bytes: EF BB BF, the UTF-8 byte order mark. Excel writes it so that it can recognize its own files later. Almost nothing else wants it, and left in place it attaches itself to the first column name, so a column called id stops matching the string id and the resulting bug survives several rounds of debugging because the header looks perfectly normal on screen.

It is stripped by default, and the report says whether the file had one. Being told that a file did have a BOM is often the whole answer to a mystery elsewhere.

Line endings are the other half of the same story. Windows tools write CRLF, Unix tools write LF, and a file that two tools have both appended to ends up with both. That mix is what puts a stray carriage return on the end of the last field of some rows, which then travels into a database as part of the value. The report names what the source used, including the case where it used both, and Line endings decides what gets written. LF is the default because it is what every modern tool reads happily, including Excel.

A bare carriage return with no newline after it, the classic Mac ending, is also detected and counted. It is rare and it breaks line counting in almost everything, so it is worth knowing about when it turns up.

When this page cannot help

The repair here undoes one specific mistake: UTF-8 read as Windows-1252 and re-saved as UTF-8. That covers the large majority of broken CSVs, and it does not cover everything.

  • A file that is genuinely single-byte encoded. If the bytes on disk really are Latin-1 or CP1252, nothing was double-encoded and there is nothing to run backwards. The characters will look wrong when the browser reads the file as UTF-8, and the fix is to re-export as UTF-8 from the system that produced it. The page says so when it finds no mojibake, rather than leaving you to wonder.
  • Double or triple mangling. A file that has been through the same mistake twice needs the repair applied twice. Run the output through the page again; the second pass finds the second layer.
  • Text that already contains replacement characters. Those diamonds mean a decoder gave up before you got the file, and the original bytes are gone. Nothing can bring them back.
  • Legacy Asian encodings. Shift-JIS, Big5, GB18030 and their relatives are multi-byte and are not the mistake this page reverses. Convert at the source.

Try every cell exists for the borderline case where the detection is too conservative: a file where the only broken characters happen to fall outside the tell-tale pairs. It is safe, because the verification still applies to every attempt, and it is slower on a large file for no benefit when the automatic mode already found everything.

Frequently Asked Questions

What is mojibake?

Text that was encoded correctly, decoded with the wrong character set, and then saved again. Almost always UTF-8 bytes read as Windows-1252 and re-saved as UTF-8, which is why cafe with an accent becomes café and a curly quote becomes three characters starting with â. The information is re-encoded rather than lost, so the mistake can be run backwards exactly.

Why do other repair tools fix the accents but not the quotes?

Because they map characters back to bytes with a simple code check and treat Windows-1252 as Latin-1. Those two encodings disagree about bytes 0x80 to 0x9F, which Latin-1 leaves as control codes and CP1252 fills with the curly quotes, the dashes, the ellipsis and the euro sign. A reverse table for that block is built in here, so the punctuation repairs too.

How do I know the repair is right and not just different?

Because it is verified. The bytes produced by running the mistake backwards are decoded with the fatal flag set, so invalid UTF-8 throws instead of quietly becoming replacement characters. A cell that fails keeps its original text and is counted separately in the report as one that looked broken but did not repair cleanly.

The page says it found no mojibake, but my characters still look wrong. Why?

Most likely the file was never double-encoded: the bytes on disk really are Latin-1 or Windows-1252, so there is nothing to reverse. The fix for that is to re-export the file as UTF-8 from whatever produced it. The other possibility is a legacy multi-byte encoding such as Shift-JIS, which this repair does not cover.

Should I strip the byte order mark?

Almost always yes, which is why it is on. Excel writes it so it can recognize its own files, and nearly everything else treats it as part of the first column name, so a column called id stops matching the string id and the bug survives a lot of debugging because the header looks normal on screen.

Which line endings should I choose?

LF unless something downstream insists otherwise. Every modern tool reads it, Excel included. The report tells you what the source file used, and it will say when a file mixed CRLF and LF together, which usually means two tools appended to it and is worth knowing on its own.

My file was mangled twice. Can it be fixed?

Yes, one layer at a time. Run the file through the page, download the result, and run that through again. The second pass finds the second layer. What cannot be recovered is text that already contains replacement characters, because at that point a decoder has already discarded the original bytes.

Does the file leave my computer?

No. There is no upload endpoint on this page. The bytes are read, repaired and rewritten by JavaScript in your own tab, and nothing is kept between visits.

Give the accents back

Free, no account, no upload. Drop the broken file, check the count of repaired cells, take the CSV.

Back to the encoding repairer