Unicode & Encoding Inspector

[ VISIBLE CHARACTERS ]
29
Something below needs a look
Code points29
UTF-16 units29
UTF-8 bytes38
[ WHAT TO LOOK AT ]
Invisible1 · U+200B

Invisible characters. They render as nothing, so the text LOOKS correct and compares unequal. A zero-width space in a password field or a BOM at the front of a CSV are the usual causes of “but it looks identical”.

Unusual space1 · U+00A0

Spaces that are not the ordinary U+0020. Almost always pasted from a word processor or a web page. They survive a trim() that only strips ASCII whitespace, and they break a split on " ".

Smart punctuation3 · U+2014 U+201C U+201D

Typographic punctuation substituted by a word processor — curly quotes, en and em dashes, a single-character ellipsis. Fine in prose, and a syntax error in code, a CSV or a JSON string.

[ CHARACTER BY CHARACTER ]
JU+004ABasic Latin1B
uU+0075Basic Latin1B
aU+0061Basic Latin1B
nU+006EBasic Latin1B
 U+00A0NO-BREAK SPACE2B
DU+0044Basic Latin1B
eU+0065Basic Latin1B
lU+006CBasic Latin1B
aU+0061Basic Latin1B
U+0020SPACE1B
CU+0043Basic Latin1B
rU+0072Basic Latin1B
uU+0075Basic Latin1B
zU+007ABasic Latin1B
U+0020SPACE1B
U+2014EM DASH3B
U+0020SPACE1B
U+201CLEFT DOUBLE QUOTATION MARK3B
dU+0064Basic Latin1B
eU+0065Basic Latin1B
vU+0076Basic Latin1B
eU+0065Basic Latin1B
lU+006CBasic Latin1B
oU+006FBasic Latin1B
pU+0070Basic Latin1B
eU+0065Basic Latin1B
rU+0072Basic Latin1B
U+201DRIGHT DOUBLE QUOTATION MARK3B
·U+200BZERO WIDTH SPACE3B
38 bytes in UTF-8. A VARCHAR limit and an HTTP header limit count BYTES, not characters, which is why a name with an ñ can be rejected by a field that looks long enough.
[ WHAT THIS IS ]

There are four answers to “how long is this”, and they are all different.

"👨‍👩‍👧" is 8 to JavaScript’s .length, 5 code points, 18 bytes in UTF-8, and 1 character to the person who typed it.

A VARCHAR(20) measures bytes. A form validator usually counts UTF-16 units. The user counts what they can see. Pick one, call it “length”, and a field that looks long enough starts rejecting somebody’s name.

Then there are the characters that are there and do not render. A zero-width space in a password. A BOM at the front of a CSV that breaks the first column header. A non-breaking space pasted out of Word. The text looks right and compares unequal.

Direction controls get their own heading here. They do not merely fail to render — they reorder the text around them, so what you read and what runs can differ.

[ QUESTIONS ]

Why are there four different lengths?

Because four different things measure "length". A VARCHAR column and an HTTP header count BYTES. JavaScript's .length counts UTF-16 units. A character limit on a form usually means code points. And the person typing counts what they can see. For "👨‍👩‍👧" those are 18, 8, 5 and 1. Picking one and calling it the length is how a field that looks long enough rejects somebody's name.

My two strings look identical but compare unequal. Why?

Usually one of three things, and this finds all of them. An invisible character — a zero-width space, a soft hyphen, a BOM at the front of the file. A space that is not U+0020, typically a non-breaking space pasted from a word processor. Or a different normalisation form: "é" can be one code point or two, and the two versions render identically and compare unequal in almost every language.

What are the direction control characters and why are they separate?

They reorder the text around them. That makes them different in kind from a character that merely fails to render: a line of source code containing U+202E can read one way and execute another, which is the Trojan Source class of attack. They get their own finding, ranked above everything except already-broken text, rather than sitting in a list beside a smart quote.

What should I do about a character it flagged?

It depends what the text is for. Smart quotes are correct in prose and a syntax error in JSON or a CSV. A non-breaking space is deliberate in "10 kg" and a bug in a database key. Astral characters are perfectly valid and only a problem if something downstream slices by .length. The tool names what is there and why it matters; whether it belongs is your call.

Why do some characters get a block name instead of a real name?

Because the full Unicode character database is megabytes and this engine ships with no dependencies at all. Every character worth warning about is named exactly. Everything else is identified by its Unicode block, which is factual and short. Inventing a plausible-sounding name would be worse than an honest one.

What is NFC and should I normalise?

NFC is the composed normalisation form, and it is what the web defaults to. If you are comparing strings, using them as keys, or storing them for a uniqueness check, normalise first — otherwise two identical-looking values will both be accepted and your duplicate check will never fire. The button appears only when the text is not already in NFC.

[ THE MATHS ]
Four measures of one string
Visible characters1
Code points5
UTF-16 units (.length)8
UTF-8 bytes18
One family emoji, four different counts. Each is the right answer to a different question.
[ NEXT ]
51Word & Character CounterCounts for ordinary text
55Text RedactorTake things out of it
42Base64 EncoderThe other encoding question
[ IMPORTANT ]

Runs entirely in your browser; nothing is transmitted. Full Unicode names are not shipped — every character worth warning about is named exactly, and the rest are identified by block rather than guessed at. Nothing you type here is sent to our servers — the calculation runs entirely in your browser.