Skip to content

What counts as one character?

Four different systems count the same text four different ways. For English prose they agree, which is why the problem stays invisible until the day it does not.

Last reviewed

One emoji, four answers

Take a thumbs-up with a skin tone applied, or the emoji of a family. Count it four ways and you get four numbers, none of which is wrong.

A reader sees one character. One thing, one press of the backspace key to delete it.

A regular expression may see seven. It is built from several code points joined by invisible connectors — a base emoji, a modifier, a joiner, another emoji, and so on.

JavaScript reports eleven. It stores text in 16-bit units, and anything outside the basic range takes two of them, so each of those code points can count double.

A database column sees twenty-five bytes. Encoded as UTF-8 for storage, each code point occupies up to four bytes.

For ordinary English prose all four numbers are identical. That is exactly why the problem is invisible until someone enters a name with an accent, a sentence in Japanese, or a single emoji — and a field that has worked for three years starts truncating data.

Which number your limit means

The practical question is never "how long is this text". It is "what is doing the limiting", and there are four common answers.

A database column declared VARCHAR counts bytes in most engines. This is why a field that comfortably accepts 255 English characters rejects far fewer Japanese ones — three bytes each — and why a name with an accent occasionally does not fit when its plain version did.

An SMS is 160 characters in its own seven-bit alphabet, and drops to 70 per message the moment one character falls outside it. A single curly quote pasted from a word processor, or one emoji, can turn a one-part message into three and triple what you are charged.

A form validator in JavaScript almost always counts UTF-16 units, which is why a field that says 280 characters may reject a string that looks much shorter when it is full of emoji.

A person counts what they can see, which is the only definition that matters for a headline, a title tag or anything with a visual budget.

Where the counts diverge

Knowing which inputs cause trouble is more useful than knowing the theory, because those inputs are predictable.

Emoji, particularly the composed ones — skin tones, families, flags, professions. A flag is two regional-indicator letters; a profession is usually a person, a joiner and an object.

Accented and combining characters. An é can be written as one code point or as an e followed by a combining acute accent. They look identical, sort differently, compare as unequal, and count differently — and both forms occur in real data, frequently in the same column.

Non-Latin scripts. Chinese, Japanese and Korean characters are three bytes each in UTF-8. Hindi, Thai, Tamil and Arabic form clusters that a reader perceives as single units and that are several code points long.

Mathematical and musical symbols, which live outside the basic range and therefore count double in UTF-16.

What to do about it

Count the thing your limit counts. A counter that shows all four numbers answers the question directly, which is faster than reasoning about it. In code, use the function that matches: Intl.Segmenter in JavaScript counts what a reader sees, len(s.encode("utf-8")) in Python counts bytes, mb_strlen rather than strlen in PHP.

Fix the column rather than the form. If a database field is the constraint, the durable answer is upstream: declare it utf8mb4 in MySQL or text in PostgreSQL. MySQL's older "utf8" is famously only three bytes per character and cannot store an emoji at all — a long-standing trap that has silently truncated a great deal of real data. Counting carefully at the form is a workaround for a column that should have been declared differently.

Normalise on the way in. Converting text to a single normal form at the point of entry means the two spellings of é become one, and comparisons, sorting and counting all start agreeing. Every language has a function for it, and doing it once at the boundary is far easier than handling both forms everywhere.

The related case: base64

Worth mentioning because it produces the same kind of surprise. Encoding data as base64 takes three bytes at a time and writes them as four characters, so the result is about a third larger — arithmetic, not inefficiency. A 6 MB attachment becomes roughly 8 MB of text, which is why a file comfortably under a size limit can be rejected once it is encoded for transit.

The same sum explains why email attachments bounce at limits they appear to satisfy. If you are sizing something against a cap, size the encoded form.

Frequently asked questions

Why does my 255-character field reject shorter text?

Almost certainly because it counts bytes rather than characters. Accented letters take two bytes in UTF-8 and CJK characters take three, so 255 bytes might be 85 Japanese characters. Declaring the column as utf8mb4 or text fixes it properly.

Does an emoji really cost me three SMS messages?

It can. A message containing only characters from the GSM alphabet gets 160 per part; one character outside it switches the whole message to a 70-character encoding. So a 150-character message with one emoji becomes three parts rather than one.

Which count should I use for a title tag?

None of them precisely — search engines truncate on pixel width rather than character count, so a title of wide capitals is cut sooner than one of narrow lowercase. Around 60 characters is the usual rule of thumb, and it is a guide rather than a limit.

Why do two identical-looking strings not match?

Usually because an accented character is written two different ways — as a single code point in one and as a base letter plus a combining mark in the other. Normalising both to the same form before comparing makes them match, and is worth doing at the point data enters your system.

Is a word count more stable than a character count?

For English, mostly. Not universally: Chinese and Japanese do not separate words with spaces, and Thai does not either, so word counting in those scripts requires segmentation and different tools give different answers.