HTML entity encoder and Unicode inspector
Turn text into HTML entities or back, see exactly which code points, bytes and characters a piece of text is made of, and get escape sequences for JavaScript, JSON, Python and CSS strings. Nothing is uploaded: it all runs in this page.
Turn text into HTML character references (&, é, 😀) or turn them back into text.
9 characters encoded: 8 as names, 1 as numbers.
Paste any text to see every code point with its UTF-8 and UTF-16 form, what a reader counts as one character, and how it changes under the four normalization forms.
- UTF-16 units
- 23
- Code points
- 18
- Characters a reader sees
- 11
- UTF-8 bytes
- 42
Code points
| # | Char | Code point | UTF-8 bytes | UTF-16 units | Category |
|---|---|---|---|---|---|
| 1 | e | U+0065 | 65 | 0065 | Ll Lowercase letter |
| 2 | ◌́ | U+0301 | CC 81 | 0301 | Mn Nonspacing mark |
| 3 | ␣ | U+0020 | 20 | 0020 | Zs Space |
| 4 | 👨 | U+1F468 | F0 9F 91 A8 | D83D DC68 | So Other symbol |
| 5 | · | U+200D | E2 80 8D | 200D | Cf Format (invisible) |
| 6 | 👩 | U+1F469 | F0 9F 91 A9 | D83D DC69 | So Other symbol |
| 7 | · | U+200D | E2 80 8D | 200D | Cf Format (invisible) |
| 8 | 👧 | U+1F467 | F0 9F 91 A7 | D83D DC67 | So Other symbol |
| 9 | ␣ | U+0020 | 20 | 0020 | Zs Space |
| 10 | 🇨 | U+1F1E8 | F0 9F 87 A8 | D83C DDE8 | So Other symbol |
| 11 | 🇦 | U+1F1E6 | F0 9F 87 A6 | D83C DDE6 | So Other symbol |
| 12 | ␣ | U+0020 | 20 | 0020 | Zs Space |
| 13 | A | U+0041 | 41 | 0041 | Lu Uppercase letter |
| 14 | ◌̊ | U+030A | CC 8A | 030A | Mn Nonspacing mark |
| 15 | ␣ | U+0020 | 20 | 0020 | Zs Space |
| 16 | Å | U+00C5 | C3 85 | 00C5 | Lu Uppercase letter |
| 17 | ␣ | U+0020 | 20 | 0020 | Zs Space |
| 18 | fi | U+FB01 | EF AC 81 | FB01 | Ll Lowercase letter |
Character names are not shown, because the name list would add a large data file to this page. The general category (letter, mark, symbol and so on) comes from your browser's own Unicode data.
Categories in this text
- Lu Uppercase letter: 2
- Ll Lowercase letter: 2
- Mn Nonspacing mark: 2
- So Other symbol: 5
- Zs Space: 5
- Cf Format (invisible): 2
What a reader counts as one character
Each box is one grapheme cluster as your browser splits it. A flag, a family emoji and a letter with an accent can each be several code points but one box.
- é2 code points
- ␣U+0020
- 👨👩👧5 code points
- ␣U+0020
- 🇨🇦2 code points
- ␣U+0020
- Å2 code points
- ␣U+0020
- ÅU+00C5
- ␣U+0020
- fiU+FB01
Normalization forms
The same visible text can be stored in different ways, for example e plus a combining accent, or one precomposed letter. Normalizing makes comparisons and searches agree.
| Form | Result | Code points | UTF-8 bytes | Compared with input | Copy |
|---|---|---|---|---|---|
| NFC | é 👨👩👧 🇨🇦 Å Å fi | 16 | 40 | Changed | |
| NFD | é 👨👩👧 🇨🇦 Å Å fi | 19 | 43 | Changed | |
| NFKC | é 👨👩👧 🇨🇦 Å Å fi | 17 | 39 | Changed | |
| NFKD | é 👨👩👧 🇨🇦 Å Å fi | 20 | 42 | Changed |
NFC and NFD only reorder or combine equivalent characters. NFKC and NFKD also replace look-alikes such as the fi ligature or a circled digit, which can change meaning, so avoid them for text you must keep exactly.
Write text as escape sequences for a JavaScript, JSON, Python or CSS string. Backslashes and control characters are always escaped.
8 characters escaped.
How it works
- •HTML entities: the first mode writes only & < > " and ' as references, which is what you need to put text inside an HTML page or a quoted attribute. The other two modes also write every non-ASCII character, or every character, for systems that only take plain ASCII. You can write them as names (é), decimal (é) or hex (é). Emoji are written as one reference, not two halves.
- •Decoding runs once, so &lt; becomes < and not <. It understands decimal and hex references and the 253 named ones described next. Numeric references follow the HTML standard: �, surrogates and anything above U+10FFFF become U+FFFD, and € to Ÿ are read as Windows-1252 characters (– is an en dash). Names need the closing semicolon here. Anything it does not know is left exactly as written and listed.
- •The name table has 253 entries: the 252 names of HTML 4.01 (accented letters, Greek letters, arrows, common symbols, , ©, €) plus '. Python's copy of the HTML5 table has 2,125 names, so there are many rare ones this page does not decode. The table was generated from that Python table by a script, not typed by hand.
- •Unicode inspector: a code point is one number in the Unicode table. JavaScript counts UTF-16 units, where an emoji takes two. Files and the web mostly use UTF-8, where a code point takes one to four bytes. What a reader counts as one character (a grapheme cluster) can be several code points, and that count comes from your browser's text segmentation. Categories (letter, mark, symbol) come from your browser's Unicode data, so a very new character may show as unassigned in an older browser. Character names are not shown, because the name list would make this page much bigger.
- •Normalization: the four forms NFC, NFD, NFKC and NFKD are produced by your browser. NFC and NFD only swap between equivalent spellings, such as e plus a combining accent and the single letter é. NFKC and NFKD also replace look-alikes, such as the fi ligature, which can change meaning.
- •Escape formats: JavaScript \uXXXX (an emoji becomes a surrogate pair), JavaScript \u{X}, JSON, Python (\xXX, \uXXXX, \UXXXXXXXX) and CSS (\X, with a space after it only when the next character would otherwise be read as part of the number). Backslashes and control characters are always escaped, and you choose which quote mark the string uses. A lone surrogate or U+0000 cannot be written in CSS or as an HTML reference, so those become U+FFFD and you are told.
- •How it was checked: the logic was compared with Python (html, unicodedata, json and ast), Perl (grapheme clusters) and Node's own parser on 156 strings, including emoji, combining marks, joiner sequences, lone surrogates and entity-like garbage, and the character categories of all 1,114,112 code points were compared with Python's. One code point differed (U+0295, which has a different category in the newest Unicode data than in Python's). A few strings were left out of some comparisons because Python or Perl have older Unicode data or older grapheme rules than your browser (Devanagari conjuncts, for example). Text longer than 200,000 characters is refused with a message instead of being processed.
Encoding is not sanitizing
Which characters are dangerous depends on where the text goes. In HTML text and quoted attributes, & < > and quotes matter, and the first mode covers them. Inside a <script> or <style> block, entities are not decoded at all, so you need JavaScript or CSS escaping there. In a link such as href, encoding does not stop a javascript: address; you must check the scheme. In an unquoted attribute, spaces and many other characters matter.
This page changes text; it does not remove tags, check URLs or understand where you will paste the result. It is not a sanitizer and does not make untrusted input safe. Use your template engine's built-in escaping for the right context, and never decode untrusted text with the Decode direction and then put it into a page as HTML.
Questions
- Is my text uploaded?
- No. Everything is calculated by code running in this page and nothing is sent anywhere. Copy happens on your device.
- Why does one visible character count as several?
- Text is stored as code points. The letter é can be one code point (U+00E9, 2 bytes in UTF-8) or an e followed by a combining accent (2 code points, 3 bytes). A family emoji of three people joined by invisible joiners is 5 code points, 8 UTF-16 units and 18 UTF-8 bytes, but a reader sees one character. The first sample in the inspector shows this. Normalizing to NFC joins the e and the accent into one letter.
- Why was ★ left as written?
- Because it is not in this page's table of 253 names. Rather than guess, it keeps the text as it was and lists it. HTML5 has more than 2,000 names and this page knows only the common ones. A numeric reference such as ★ always works.
- Does using the first mode make my page safe from cross-site scripting?
- Only when the text goes into HTML text or a quoted attribute. It does nothing for script blocks, style blocks, URLs or unquoted attributes, and it is no substitute for your framework's escaping. See the section above.