data · 7 min read

What UTF-8 actually is, and what a BOM is for

One encoding won, for a reason worth understanding — and then there is the three-byte marker that fixes Excel and confuses everything else.

Text in a file is bytes. Something has to decide which byte means which letter, and for about thirty years everybody decided differently — a different encoding per region, all using the same byte values for different characters.

Unicode fixed the first half by assigning a number to every character in every script. UTF-8 fixed the second half by choosing how to write those numbers as bytes, and it did it in a way that made adoption almost free.

Two ideas, kept separate

Unicode is a catalogue. The letter A is 65, the Arabic letter alef is 1575, an emoji is somewhere above 128,000. Just numbers, no storage.

UTF-8 is a way of storing those numbers. UTF-16 and UTF-32 are other ways. A file described only as "Unicode" has not told you the thing you need to read it, which is why that word on an encoding menu is unhelpful.

Why UTF-8 won

It uses one byte for the first 128 characters — exactly the ASCII set, with exactly the ASCII values.

Which means every ASCII file already was a UTF-8 file, unchanged, and every program that handled ASCII kept working. Nothing had to be converted and nothing had to be rewritten on the day of the switch. That backwards compatibility is the whole reason it displaced the alternatives.

Beyond ASCII it uses two bytes for most European and Middle Eastern scripts, three for most Asian ones, and four for emoji and rarer scripts. Text that is mostly English costs almost nothing; text that is mostly Chinese costs 50% more than UTF-16 would, which is the trade that was accepted.

It also has a property that keeps parsers honest: the leading byte of a character says how many bytes follow, so you can jump into the middle of a file and find the next character boundary. UTF-16 cannot do that.

The byte order mark

UTF-16 comes in two flavours depending on which end of a two-byte number goes first, so it needs a marker at the start of the file to say which. That marker is the byte order mark.

UTF-8 has single bytes and therefore no byte order to mark. It does not need one.

It got one anyway, as a signature: the three bytes EF BB BF, meaning "this file is UTF-8". And that turned out to be genuinely useful for a format with nowhere to declare its encoding — which is to say, for CSV.

Why Excel needs it and parsers hate it

Double-click a UTF-8 CSV without a BOM in Excel and non-ASCII text is mangled, because Excel assumes a legacy regional encoding. Add the BOM and it opens correctly. This is the single most practical fact in this article.

The cost is that everything else sees three unexpected bytes before the first character. A parser reading the header row finds the first column named \uFEFFid instead of id, and the lookup fails with an error that shows two identical-looking strings.

That is what utf-8-sig is for: read with it and the mark is stripped; write with it and the mark is added. Plain utf-8 leaves it alone in both directions.

So the rule is about the destination. A file for a spreadsheet gets a BOM; a file for a program does not. When it is both, add it and tell the program to expect it.

Mojibake, and why it is recoverable

When you see أحمد or café, UTF-8 bytes have been read as a single-byte encoding. Each real character has become the two or three characters its bytes mean in that other scheme.

The important part: nothing has been lost. The bytes on disk are correct and the reader is wrong, so telling the reader the right encoding restores the text exactly.

It becomes unrecoverable only when the mangled version is saved. At that point the wrong characters are the real content, and undoing it means reversing the specific misinterpretation — which works when you can identify it and not otherwise.

Practical rules

Declare it wherever you can: <meta charset="utf-8"> as the first thing in an HTML head, charset=utf-8 in a content type header, UTF-8 as the database and connection encoding.

In MySQL, use utf8mb4 rather than utf8. The one called utf8 is a three-byte subset that cannot store emoji or several scripts, and it is a long-standing trap.

Never assume the encoding of a file you were sent. Look at it, or ask.

The short version

Unicode numbers the characters; UTF-8 stores those numbers as bytes, staying byte-identical to ASCII for the first 128. A BOM is an optional three-byte signature that Excel relies on and parsers trip over. And mojibake is a reading error rather than damage — until someone saves it.

Questions

What is UTF-8?

A way of storing text as bytes. Every character has a number assigned by Unicode, and UTF-8 is one scheme for writing those numbers down — using one byte for the original ASCII set and two to four for everything else. It is what almost the entire web uses.

What is UTF-8 with BOM?

A byte order mark is three bytes (EF BB BF) at the very start of a file that say "this is UTF-8". UTF-8 does not need one technically — it has no byte order to mark — but Excel uses it to decide how to read a CSV, so it is the difference between a file opening correctly on a double-click and opening as gibberish.

What is utf-8-sig in Python?

The same thing, named from the other direction. Reading with utf-8-sig strips a byte order mark if one is present; writing with it adds one. Plain utf-8 leaves the mark in place on read, which is why an unexplained character appears at the start of your first field.

Why does my text show as أحمد or similar?

UTF-8 bytes read as though they were a single-byte encoding. Each real character becomes the two or three characters its bytes happen to mean in that other encoding. Nothing is damaged — the bytes are correct and the reader is wrong — so telling the reader the right encoding recovers it exactly.

Is UTF-8 the same as Unicode?

No. Unicode assigns a number to every character; UTF-8 is one way of turning those numbers into bytes. UTF-16 and UTF-32 are others. Saying a file is "Unicode" does not say how it is stored, which is the part a reader needs to know.

Tools from this guide