Unicode Character Inspector
Break text into code points, spot invisible and confusable characters, and generate escape sequences — for debugging text that looks right and behaves wrong.
Two strings can look identical and not be equal
The letter é can be written as one code point, or as a plain e followed by a combining acute accent that draws itself over whatever came before. Both render as é. Every font shows them the same. Paste each into the box above and the difference appears immediately:
| Spelling | Shows as | Length | Code points |
|---|---|---|---|
| composed | é | 1 | U+00E9 |
| decomposed | é | 2 | U+0065 U+0301 |
They are not equal, they do not have the same length, and a search for one will not find the
other. This is the most useful thing an inspector tells you, precisely because it is invisible
by construction: if a name will not match, a search returns nothing, or a duplicate check lets
two identical-looking rows through, this is the first thing to test.
It is not exotic either. All 27 of the accented letters common in Western
European languages split into more than one code point — every single one. Plain
unaccented letters have nothing to split, which is why this only ever bites text that crossed
a system boundary: one keyboard layout, one operating system's file names, one export.
The fix is a single call. Normalising both sides to the same form before comparing makes them
equal, and the composed form — NFC — is the one to pick, because it is what nearly
all text on the web already uses.
Four normal forms answering two questions
C or D decides whether accents are folded into single characters or split apart. Adding K decides whether characters that are merely formatted differently are also merged. Here is one string — an fi ligature followed by "ancé" — through all four:
| Form | Result | Length |
|---|---|---|
| NFC | fiancé | 5 |
| NFD | fiancé | 6 |
| NFKC | fiancé | 6 |
| NFKD | fiancé | 7 |
K is lossy, and that is the point of it. It is right for a search index, where a circled numeral and a plain one should match, and wrong for storing what somebody actually typed:
| Character | Under NFKC | Code points |
|---|---|---|
fullwidth a — a | a | U+0061 |
circled one — ① | 1 | U+0031 |
the fi ligature — fi | fi | U+0066 U+0069 |
one half — ½ | 1⁄2 | U+0031 U+2044 U+0032 |
The last row is worth checking rather than assuming, and it corrected this page's author. One
half under NFKC does not become the ASCII 1/2 — it becomes
1⁄2, using U+2044 FRACTION SLASH. NFKC merges compatibility
characters; it does not convert anything to ASCII, and those are different jobs. If you were
normalising in order to get plain ASCII out, that is a separate step you still have to write.
And normalising will not save you from lookalikes
The other reason two identical-looking strings differ is that one of them is not in the alphabet you think it is:
| Latin | Lookalike | Which letter | Merged by any form? |
|---|---|---|---|
a U+0061 | а U+0430 | Cyrillic a | no |
o U+006F | ο U+03BF | Greek omicron | no |
p U+0070 | р U+0440 | Cyrillic er | no |
e U+0065 | е U+0435 | Cyrillic ie | no |
x U+0078 | х U+0445 | Cyrillic ha | no |
Not one of them, under any of the four forms — and that is correct behaviour rather than a gap. A Cyrillic a is a genuinely different letter that happens to share a shape with the Latin one, so there is nothing to merge. Compare it with the fullwidth a in the table above, which is the same letter formatted differently and does fold.
That is the dividing line worth carrying away. Normalisation is about spelling one character consistently. Telling apart different characters that look alike is a separate problem, and reading the code points — which is what this page is for — is how you do it.
How to use
- Paste the text you want to inspect.
- Read each character's code point, name and category.
- Look for invisible or bidirectional characters flagged in the output.
- Copy an escape sequence for use in code.
Frequently asked questions
What is a code point?
The number Unicode assigns to a character, written as U+ followed by hexadecimal digits. Capital A is U+0041. It identifies the character abstractly, separate from how it is stored in bytes or drawn on screen.
Why does my string length not match what I see?
Because a visible character is not always one unit of storage. Characters beyond the basic range take two units in JavaScript and Java, and a single visible emoji may combine several code points with joiners. A family emoji can report a length of eleven.
What are confusable characters?
Characters from different scripts that look identical — Latin a and Cyrillic a, for instance. They are the basis of homograph attacks, where a domain name looks legitimate but contains a substituted letter. Inspecting the actual code points is the only reliable way to tell.
What is a zero-width character?
A character occupying no visual space, such as the zero-width space, joiner and non-joiner. Some are essential in Arabic and Indic scripts, and some are used to fingerprint copied text or to smuggle data past filters. Either way they break comparison and search invisibly.
What is Unicode normalisation?
The process of resolving characters that can be written more than one way. An accented e can be a single code point or a plain e followed by a combining accent — visually identical, byte-wise different. Normalising to a common form before comparing avoids strings that look equal and are not.
What is a bidirectional override?
A control character that reverses the display order of text, needed for mixing Arabic or Hebrew with Latin script. It can also be abused to make source code display differently from how it compiles, which was published as the Trojan Source vulnerability in 2021.
🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.