You type a letter and it appears on screen. Simple — until an emoji counts as two characters, a pasted résumé becomes “résumé,” and a friend’s name won’t display at all. All of it comes down to character encoding: the rules for turning letters into numbers into bytes. Here’s the whole story, in plain terms.
Short answer: ASCII is the original 128-character set. Unicode gives every character in every language a unique number. UTF-8 stores those numbers as bytes — one for plain English, more for everything else. Almost everything today is UTF-8, and mismatches are what cause “garbage text.”
Layer 1: characters become numbers
Computers only store numbers, so text needs a lookup table: which number means which character.
ASCII was that table for decades — 128 characters (the numbers 0–127), covering the English alphabet, digits, punctuation, and a few control codes. It fit in 7 bits, and for English it was enough. The problem is obvious the moment you leave English: no é, no ñ, no Korean, no emoji.
Unicode is the fix. It’s one giant table with room for over a million characters and assigns every one a unique number called a code point (written like U+00E9 for é). More than 150,000 are defined so far — every living script, historical ones, math symbols, and yes, emoji. Crucially, Unicode’s first 128 code points are identical to ASCII, so the old basics still line up.
Layer 2: numbers become bytes
Knowing that é is code point U+00E9 doesn’t say how to store it. That’s a separate job called an encoding, and the winner is UTF-8.
UTF-8 is variable-length:
| Character type | Bytes in UTF-8 |
|---|---|
| Plain ASCII (A, 7, space) | 1 |
| Accented / most European (é, ñ) | 2 |
| Korean, Chinese, Japanese, Devanagari | 3 |
| Most emoji | 4 |
This is the clever part: English text stays exactly as small as old ASCII (one byte each), but the same file can hold any language or an emoji when needed. It’s backward-compatible with ASCII and now used by the overwhelming majority of the web — which is why “save as UTF-8” is almost always the right answer.
Why an emoji “counts as two”
This byte story explains a daily mystery. Many systems — including mainstream programming languages (JavaScript, Java, and .NET all count this way) and some social platforms — count text in UTF-16 code units, where a character outside the basic range takes two units. So a single 😀 registers as 2, and a flag or skin-tone emoji as 4 or more.
Different tools count differently: bytes, code points, or code units. The Character Counter counts code points — closest to what you actually see as characters — so a plain emoji shows as 1. When a platform’s limit disagrees, it’s usually counting UTF-16 units instead, which is why social media character limits can bite even when your counter says you’re under.
Why text turns to garbage (“é”)
“Mojibake” — that mess of é, ’, or  — is an encoding mismatch. The text was saved as UTF-8, where é is the two bytes C3 A9, but something opened it as an old single-byte encoding and drew those two bytes as two separate characters (à and ©). Nothing is corrupted; it’s just being read with the wrong table. Re-open or re-import the file as UTF-8 and it snaps back.
Where this shows up
- CSV and spreadsheet imports are the classic offender — the importer guesses the wrong encoding. Specify UTF-8 on import.
- APIs and config files should be UTF-8 end to end; a mismatch shows up as garbled names. When you’re eyeballing a payload, the JSON Formatter makes the structure readable so you can spot the bad characters.
- Binary data as text is a related idea: when a file has to travel through a text-only channel, it gets turned into safe ASCII characters — that’s what Base64 encoding is for.
The one rule
Use UTF-8 everywhere, and open files as the encoding they were saved in. Do that and “just text” stays just text — no counting surprises, no é, no missing names. Encoding is invisible when it’s consistent; it only becomes a headache when two systems quietly disagree about which table they’re using.