Character Encoding Explained And Why Text Arrives Broken
The bytes are almost always fine. What broke is the agreement about how to read them.
Character encoding is the table that turns bytes into letters. A file holds bytes. It does not hold letters. Turning bytes into letters needs a table saying which number means which character. A file rarely says which table it was written with. Every garbled name, every question mark where an accent should be, comes from that one gap.
This turns up wherever text moves between programs. A CSV that opens wrong in Excel, a subject line that arrives as symbols, a name with an accent that survives one system and not the next. The bytes are almost always fine. The agreement about reading them is what broke.
The Character Encoding Table Nobody Sends With the File
Start with what a byte can hold. One byte gives 256 possible values. English needs about a hundred characters, so the early agreement, ASCII, used half the range and left the rest unused.
That spare half became the problem. Every region filled it differently.
| The byte | Under ASCII | Under Windows-1252 | Under ISO 8859-7 |
|---|---|---|---|
| 0x41 | A | A | A |
| 0x65 | e | e | e |
| 0xE9 | undefined | e with an acute accent | a Greek letter |
| 0xF6 | undefined | o with an umlaut | a different Greek letter |
Nothing inside a plain text file names its encoding. There is nowhere to put one. A program opening it looks for a declaration somewhere outside the file. Failing that it guesses. Failing that it falls back to a regional default set years ago on a machine nobody remembers.
What Unicode Changed About Character Encoding
The Unicode Consortium broke the problem in two. Holding those halves apart clears up most of the confusion.
Unicode, the List
- Every character in every script
- One number each, called a code point
- The letter A is always 65
- Says nothing about bytes
UTF-8 and UTF-16, the Ways to Write It
- Turn each number into bytes
- UTF-8 uses one to four per character
- UTF-16 uses two or four
- Same characters, different bytes
UTF-8 won the web on one property. Plain English under UTF-8 is byte for byte identical to ASCII, so decades of existing text became valid UTF-8 without anybody touching it.
the word cafe, with an accent on the e
ASCII 63 61 66 65 ?? the accent has no byte
Windows-1252 63 61 66 E9 one byte, meaning fixed by region
UTF-8 63 61 66 C3 A9 two bytes, meaning fixed everywhere
read the UTF-8 bytes as Windows-1252
c a f A-tilde (c)
cafA(c) the classic wrong result
Why Garbled Text Looks the Way It Does
Mojibake is not random. It follows the tables exactly, which is why the same wrong pairs appear over and over.
| What you see | What happened |
|---|---|
| A capital A with a tilde before a symbol | UTF-8 read as Windows-1252 |
| A black diamond with a question mark | The program gave up and substituted |
| A single question mark where a letter was | Converted to an encoding with no room for it |
| Chinese or Cyrillic in an English document | A two byte encoding read as one byte. Or the reverse |
| Every other character is a blank box | Encoding is right, the font has no glyph |
The last row is not an encoding problem at all. Boxes mean the text decoded correctly and the font has nothing to draw. Changing the encoding will not help. This distinction saves a great deal of wasted effort.
The diamond question mark is the one that cannot be undone. Every unreadable byte becomes that same character, so the original values are gone rather than misread. Everything else on this list is recoverable from the original file, which is the argument for never overwriting it.
Two Different Things Both Called Encoding
This is where the word does real damage, because the same term covers two problems that have nothing to do with each other.
Character encoding
What does this byte meanUTF-8
Windows-1252
ISO 8859-1
UTF-16
decides which letter
a byte stands for
Get this wrong and the wrong letters appear. The bytes are unchanged.
Transfer encoding
How do these bytes survivebase64
quoted-printable
decides how bytes cross
a channel that only
carries plain text
Get this wrong and you see a wall of characters rather than a file.
Mail keeps the two visible side by side. The transfer layer is why an attachment arrives a third larger than the file. The character layer is why a subject line in French sometimes arrives as symbols. MIME carries both declarations in the same header block.
Read next Why Email Attachments Are Encoded And What It Costs The other encoding. What it adds to everything you send.Where a File Declares Which Character Encoding It Uses
Some formats declare it. Some cannot. Knowing which is which explains why certain files give trouble and others never do.
| Format | Declares its encoding |
|---|---|
| HTML and XML | Yes, in a tag near the top |
| Yes, a charset on every text part | |
| JSON | UTF-8 by the specification, no declaration needed |
| Plain text and CSV | No. Nothing anywhere |
| Word and modern Office files | Yes, inside the package |
| Yes, per font and per text object |
CSV is the format that causes the most of this. It has no header, no declaration and no specification worth the name. Every program opening one has to guess. Excel falling back to a regional default is behind most spreadsheet trouble people meet.
The Byte Order Mark And Why It Divides People
Since a file cannot declare its encoding, somebody proposed marking the front of it instead. That is the byte order mark.
It works. It also creates a second problem. Software that expects content at byte zero now finds three unexpected ones. The results range from an invisible character at the start of a heading to a script that refuses to run.
-
Windows toolsWant itExcel in particular reads a UTF-8 CSV correctly when the mark is present and often wrongly when it is not -
Web and Unix toolsDo not want itTreated as content, so it appears as a stray character or breaks a file that must start with something specific -
The practical ruleDepends on the readerInclude it where a Microsoft program has to open the file. Leave it out everywhere else
Reinterpreting Versus Converting And Why It Matters
Every editor that handles encodings offers two commands that sound alike. Choosing the wrong one is how a recoverable file becomes an unrecoverable one.
Reinterpret
Notepad++ calls this Encode inbytes unchanged
table changed
read the same bytes
against a different
table
The one you want while diagnosing. Try a table, look at the result, try another. Nothing is written.
Convert
Notepad++ calls this Convert tobytes rewritten
table changed
write new bytes for
the characters
currently on screen
Correct only once the text already reads properly. Run it on garbled text and the garbling is written into the file.
This is how files get genuinely damaged. Somebody opens a file that looks wrong and saves it as UTF-8 hoping that helps. The editor faithfully writes out the characters it was showing. The original byte values are gone. No later effort recovers them, which is the real reason for working on a copy.
When the Encoding Has Been Wrong Twice
Sometimes the garbling has been through the mill more than once. That shows.
correct Federation, with an accent
once wrong FA(c)deration
twice wrong FAA(c)A(c)deration
Each pass takes the wrong characters and encodes those, so the damage compounds and the string grows every time. It is still reversible. It needs the same number of passes back that it took going forward.
Long runs of accented capitals are the signature. One or two odd characters where an accent belongs is a single misreading. A cluster of five or six is double encoding. It tells you the file passed through two systems that each guessed differently.
Working Out Which Character Encoding a File Uses
- Ask the editor what it thinks. Notepad++ shows the encoding in the status bar and VS Code in the bottom right corner. On Mac or Linux the file command reports it. That is a guess rather than a reading. It is still a better starting point than your own.
- Look at where the text goes wrong. Ordinary English fine and accents broken points at a one byte encoding read as UTF-8 or the reverse. Everything broken points at something more serious.
- Try UTF-8 first. It is the likeliest answer by a wide margin. Text coming out clean means you are finished.
- Then try the regional default. Windows-1252 for western European text. The matching code page for anything else. Between those two you have covered most files.
- Check the first three bytes. EF BB BF means a UTF-8 mark is present, which both tells you the encoding and explains a stray character at the start.
- Keep the original. Work on a copy every time. Once a program has written the diamond question mark into a file, no amount of later effort brings the characters back.
Automatic detection is a guess and the standard says so. It works by statistics rather than by reading a declaration, so it is right most of the time and confidently wrong the rest. Where a declaration exists, believe it over any detector.
Where the file in question is a mailbox rather than a spreadsheet, the encoding is declared per part and a reader that honours those declarations shows the text as intended. Our file viewers do that in a browser tab. The converters carry the declarations through rather than flattening everything to one guess.
Encoding behaviour and byte level examples checked against the Unicode standard and the referenced articles in August 2026.
Questions People Ask
7 questions, answered in full below.Why does my file show question marks or odd symbols?
Because it was written under one agreement about what the bytes mean and opened under another. Nothing is damaged. The same bytes are being read against the wrong table, which is why the wrong characters appear in exactly the right places.
Is UTF-8 the same as Unicode?
No. The distinction is the useful one. Unicode is the list of characters and the number each one gets. UTF-8 is one way of turning those numbers into bytes. UTF-16 is another way of turning the same numbers into different bytes.
Why does Excel mangle my CSV?
Because a CSV carries no statement of its own encoding, so Excel has to guess and its guess is often a regional default rather than UTF-8. Saving as CSV UTF-8 rather than plain CSV usually settles it. So does opening through the import dialogue, where you can name the encoding yourself.
What is a BOM and should I use one?
A few bytes at the start declaring which encoding follows. It helps Windows tools guess correctly and it upsets a good deal of software that expects a file to begin with content. Use it where a Microsoft program has to read the file, avoid it elsewhere.
Can mojibake be reversed?
Often, since the original bytes usually survive. Where a program has already replaced unreadable bytes with the diamond question mark, the information is gone for good, because every failure becomes that same character. That is why keeping the original file matters more than fixing a copy.
Is base64 a character encoding?
No. The shared word causes real confusion. Base64 converts bytes into a small set of safe characters so they survive a journey. Character encoding decides what a byte means in the first place. Different problems, unrelated solutions.
What should I use for new files?
UTF-8, without exception worth arguing about. It covers every script, it is what the web overwhelmingly uses and plain English text comes out identical to ASCII. The only real question is whether a particular Microsoft program on the receiving end needs a BOM.
Sources
Where the figures and behaviour described above were checked.
- Character encoding Wikipedia
- Mojibake Wikipedia
- Charset detection Wikipedia