Unicode Character Lookup
Search and analyze Unicode characters. View code points, UTF-8 and UTF-16 encoding, HTML entities, CSS escape sequences, and character categories for any text input.
A Unicode Character Lookup is the fundamental process of identifying, translating, and managing the standardized numerical values assigned to every letter, symbol, and emoji across all human languages. At its core, computers do not understand text; they only understand binary numbers, making a universal translation system absolutely critical for global digital communication. By mastering how characters are mapped to specific codes, developers and digital professionals can ensure that a Japanese kanji, an Arabic letter, or a smiling emoji sent from a smartphone in Tokyo renders flawlessly on a database server in New York.
What It Is and Why It Matters
At the most fundamental level of computer science, hardware operates entirely on binary code—sequences of zeros and ones. Because microprocessors have no inherent concept of human language, alphabets, or punctuation, software must utilize a translation map to convert human-readable text into machine-readable numbers. A Unicode Character Lookup is the process of querying the universal standard map, known as the Unicode Standard, to find the exact numerical identifier (called a code point) assigned to a specific character. For example, when you type the capital letter "A", the computer looks up its designated number, which is 65 in decimal format, and stores it in binary. When you retrieve that data, the computer reverses the process, looking up the number 65 and rendering the visual shape of the letter "A" on your screen.
Before the widespread adoption of Unicode, the digital landscape was heavily fragmented by hundreds of conflicting encoding systems. A text file written in Russian using one encoding standard would appear as a completely unreadable string of random symbols if opened on a computer utilizing an American or Japanese encoding standard. This phenomenon, known as "Mojibake," caused catastrophic data corruption in early global networks, banking systems, and international communications. Unicode solves this problem by providing a single, comprehensive, and universally accepted registry where every character from every language—both living and dead—has a unique, unchangeable number.
Understanding Unicode Character Lookup is not merely an academic exercise; it is a critical skill for anyone involved in software development, data science, or digital typography. When a developer builds a web application, they must configure their databases, application logic, and front-end code to handle these universal numbers correctly. Failure to understand how characters are looked up, encoded, and stored leads to broken user interfaces, security vulnerabilities, and corrupted datasets. Whether you are validating user input, parsing a massive 10,000-row CSV file containing international customer names, or simply trying to figure out why an emoji is breaking your layout, a deep understanding of Unicode ensures your digital infrastructure remains robust, inclusive, and globally compatible.
History and Origin
To truly understand the elegance of Unicode, one must examine the chaotic history of early computing that necessitated its invention. In the 1960s, the American National Standards Institute (ANSI) developed the American Standard Code for Information Interchange (ASCII). Published in 1963, ASCII was a 7-bit character encoding system that could represent exactly 128 characters. This was perfectly sufficient for early American mainframes, as it included the English alphabet (uppercase and lowercase), numbers 0 through 9, basic punctuation, and a set of invisible control characters used to manage teletype machines. However, because ASCII only used 7 bits, it left no room for accented characters, let alone entirely different writing systems like Cyrillic, Greek, or Arabic.
As personal computing expanded globally in the 1980s, the limitations of ASCII became a massive bottleneck. To accommodate other languages, computer manufacturers began using the 8th bit of a standard byte (which doubled the capacity from 128 to 256 characters) to create localized "code pages." The International Organization for Standardization (ISO) released the ISO-8859 family of standards, where ISO-8859-1 covered Western European languages, ISO-8859-5 covered Cyrillic, and so on. The fatal flaw in this system was that the exact same numerical value meant completely different things depending on which code page the computer was actively using. The number 233 might represent the letter "é" in France, but a Cyrillic "щ" in Russia. If a user forgot to specify the correct code page, the text was hopelessly corrupted.
The movement to unify these disparate systems began in the late 1980s. In 1987, Joe Becker from Xerox, alongside Lee Collins and Mark Davis from Apple, began investigating the practicalities of creating a single, universal character set. Becker coined the term "Unicode" in a 1988 draft proposal, envisioning a 16-bit system capable of handling 65,536 characters—which he believed was enough to encode all modern written languages. In October 1991, the Unicode Consortium published Unicode 1.0, which contained exactly 7,161 characters, heavily focusing on Latin, Greek, Cyrillic, Hebrew, Arabic, and Han ideographs. By 1996, with the release of Unicode 2.0, the architects realized 65,536 spaces would not be enough to hold all historic scripts and future symbols, so they expanded the architecture to support over 1.1 million distinct characters. Today, the Unicode Standard is continuously updated by a dedicated consortium of linguists and engineers, encompassing over 149,000 characters and serving as the bedrock of modern digital communication.
Key Concepts and Terminology
To navigate the world of character encoding, you must first build a precise vocabulary, as colloquial terms like "letter" or "character" are far too ambiguous for computer science. The most important concept is the Code Point. A code point is the specific, abstract numerical value assigned to a character in the Unicode standard. It is conventionally written in hexadecimal format, prefixed with "U+". For example, the code point for the capital letter "A" is U+0041, and the code point for the smiling face emoji (😀) is U+1F600. The Unicode space is divided into exactly 1,114,112 available code points, ranging from U+0000 to U+10FFFF.
Because the Unicode space is so massive, it is organized into 17 distinct sections called Planes, each containing 65,536 code points. The most important of these is Plane 0, known as the Basic Multilingual Plane (BMP). The BMP contains almost all characters required for modern, everyday writing across nearly every language in the world, spanning from U+0000 to U+FFFF. Plane 1 is the Supplementary Multilingual Plane (SMP), which houses historic scripts, musical symbols, and, most notably, modern emojis. Plane 2 is the Supplementary Ideographic Plane (SIP), dedicated to rare and historic CJK (Chinese, Japanese, and Korean) ideographs. Most developers will spend their entire careers working exclusively within the BMP and the SMP.
Another critical distinction must be made between a Character, a Glyph, and a Grapheme Cluster. A "character" is the abstract concept of a text element, such as "lowercase a". A "glyph" is the actual visual representation or drawing of that character, which is determined by the font file you are using (e.g., Arial vs. Times New Roman). Finally, a "grapheme cluster" is what a human end-user perceives as a single visual unit of text, which might actually be composed of multiple underlying code points. For example, the letter "é" can be represented as a single code point (U+00E9), or it can be a grapheme cluster made of two code points: the base letter "e" (U+0065) followed by an invisible combining acute accent (U+0301). Understanding this distinction is vital for accurately counting the length of strings in programming languages.
How It Works — Step by Step
Understanding how a human-readable character is transformed into a binary sequence stored on a hard drive is the ultimate test of Unicode mastery. The Unicode standard assigns the number (the code point), but it does not dictate how that number is physically saved in memory. That job belongs to an encoding algorithm, the most popular of which is UTF-8. UTF-8 is a variable-length encoding system, meaning it uses anywhere from 1 to 4 bytes (8 to 32 bits) to store a single character. The algorithm uses a specific mathematical formula to determine how many bytes are needed and how to distribute the binary bits of the code point into those bytes.
The UTF-8 conversion formula relies on predefined bit templates based on the numerical size of the code point. If the code point falls between U+0000 and U+007F (the traditional ASCII range), it uses 1 byte with the template 0xxxxxxx. If it falls between U+0080 and U+07FF, it uses 2 bytes: 110xxxxx 10xxxxxx. If it is between U+0800 and U+FFFF, it uses 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx. Finally, for code points between U+10000 and U+10FFFF, it uses 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. The "x" spaces represent the actual binary bits of the code point, while the leading ones and zeros (like 1110 and 10) are control headers that tell the computer how to read the sequence.
A Full Worked Example: Encoding the Euro Sign (€)
Let us manually calculate the UTF-8 byte sequence for the Euro sign (€).
- Find the Code Point: The Unicode lookup for the Euro sign reveals its code point is U+20AC.
- Convert to Binary: The hexadecimal value
20ACmust be converted to binary. Hex2is0010,0is0000,Ais1010, andCis1100. Combined, the binary value is0010 0000 1010 1100(16 bits). - Determine the Byte Length: The hex value
20ACfalls squarely in the range between U+0800 and U+FFFF. According to our formula, this requires a 3-byte template:1110xxxx 10xxxxxx 10xxxxxx. Notice there are exactly 16 "x" slots in this template. - Distribute the Bits: We take our 16 binary bits (
0010000010101100) and slide them into the "x" slots from right to left.- The last 6 bits (
101100) go into the third byte:10101100. - The middle 6 bits (
000010) go into the second byte:10000010. - The first 4 bits (
0010) go into the first byte:11100010.
- The last 6 bits (
- Convert Back to Hex: Now we translate our three binary bytes back into hexadecimal to see how it is stored on the hard drive.
1110 0010becomesE2.1000 0010becomes82.1010 1100becomesAC. Therefore, the UTF-8 encoded sequence for the Euro sign (€) isE2 82 AC. A computer reading this file will encounterE2, recognize the1110prefix, instantly know it needs to read the next two bytes to complete the character, and successfully decode the Euro sign.
Types, Variations, and Methods
While the Unicode Consortium defines the universal code points, there are several different encoding methods (known as Unicode Transformation Formats, or UTFs) used to implement them in software. The undisputed king of the modern web is UTF-8. As demonstrated in the step-by-step example, UTF-8 uses a variable length of 1 to 4 bytes. Its greatest advantage is strict backward compatibility with ASCII. Any standard English text saved in ASCII is already perfectly valid UTF-8, requiring exactly 1 byte per character. This efficiency makes UTF-8 the default standard for internet protocols, HTML documents, and Linux operating systems, as it saves massive amounts of bandwidth when transmitting Western text.
UTF-16 is another highly prevalent encoding method, natively used by the Microsoft Windows operating system, the Java programming language, and JavaScript. UTF-16 encodes characters using either one or two 16-bit units (2 or 4 bytes). Characters in the Basic Multilingual Plane (U+0000 to U+FFFF) are encoded directly as a single 16-bit integer. However, to represent characters in the higher planes (like emojis), UTF-16 uses a system called "Surrogate Pairs." It takes two 16-bit units—a high surrogate and a low surrogate—and combines them to represent a single character. While UTF-16 is highly efficient for Asian languages (which take 3 bytes in UTF-8 but only 2 bytes in UTF-16), it is prone to endianness issues (byte order confusion) and wastes space for standard English text.
UTF-32 is a fixed-length encoding where every single character, regardless of what it is, takes up exactly 4 bytes (32 bits). The advantage of UTF-32 is mathematical simplicity; developers do not need to parse variable-length byte sequences or deal with surrogate pairs. If you want to find the 100th character in a UTF-32 string, you simply multiply 100 by 4 bytes and jump directly to that memory address. However, the catastrophic downside is memory consumption. An English document saved in UTF-32 will be exactly four times larger than the same document saved in UTF-8. Consequently, UTF-32 is almost never used for data storage or network transmission, though it is occasionally used in internal memory processing where performance is prioritized over RAM usage.
Beyond raw byte encodings, developers frequently use HTML Entities and URL Encodings as alternative lookup methods. In HTML, certain characters like < and > are reserved for code syntax. To display them as text, you must use an HTML entity lookup, such as < or <. Similarly, URLs cannot contain spaces or special Unicode characters. A URL encoding lookup translates these characters into a percent-encoded format. For example, a space becomes %20, and our previously calculated Euro sign (E2 82 AC in UTF-8) becomes %E2%82%AC in a web address.
Real-World Examples and Applications
The practical application of Unicode Character Lookups is a daily reality for software engineers, data analysts, and digital designers. Consider a web developer building a global e-commerce platform. When a customer in Germany enters their shipping address containing the letter "ß" (Eszett, U+00DF), the developer must ensure the front-end form captures this correctly, the API transmits it securely via JSON, and the backend database stores it without corruption. If the database is misconfigured to use a legacy encoding like Latin-1, the "ß" might be permanently corrupted into an unreadable symbol. By standardizing the entire technology stack on UTF-8, the developer ensures the address prints correctly on the physical shipping label.
Another fascinating real-world application involves the rendering of modern emojis on mobile devices. Emojis are not static, pre-drawn images; they are dynamic Unicode sequences. Take the "Family: Man, Woman, Boy" emoji (👨👩👦). A Unicode lookup reveals this is not a single character, but a complex sequence of seven distinct elements. It consists of the Man emoji (U+1F468), a Zero Width Joiner (U+200D), the Woman emoji (U+1F469), another Zero Width Joiner (U+200D), and the Boy emoji (U+1F466). The Zero Width Joiner is an invisible control character that instructs the operating system's text rendering engine to glue the surrounding characters together into a single, unified glyph. If an application does not support advanced Unicode rendering, the user will simply see three separate emojis side-by-side.
Data scientists also rely heavily on Unicode normalization when cleaning massive datasets. Imagine an analyst compiling a list of 500,000 customer names from various international sources. One system might output the name "José" using the precomposed character "é" (U+00E9), while an Apple iOS device might output "Jose" using the base letter "e" (U+0065) followed by a combining acute accent (U+0301). Visually, these look identical on screen. However, to a computer, they are completely different binary strings. If the data scientist attempts to run a search query or a deduplication script, the computer will treat them as two distinct people. By using a Unicode lookup script to normalize all text into a standardized format, the analyst ensures data integrity and accurate reporting.
Common Mistakes and Misconceptions
One of the most pervasive misconceptions in programming is the assumption that one character always equals one byte. This fallacy stems from the decades-long dominance of ASCII. A novice programmer might write a routine that allocates exactly 50 bytes of memory to store a 50-character username. If a user inputs their name using Japanese Kanji or emojis, the string will require up to 200 bytes in UTF-8. The program will either truncate the user's name, corrupt the data, or crash entirely due to a buffer overflow. Developers must fundamentally decouple the concept of "string length in bytes" from "string length in human-readable characters."
A similarly dangerous mistake occurs when developers confuse Unicode with typography. Unicode defines what a character is, not how it looks. A common complaint on beginner forums is: "I looked up the Unicode for a specific hieroglyph, but when I print it to my screen, it just shows an empty square box!" That empty square is affectionately known as "tofu." It occurs because the operating system successfully decoded the Unicode character, but the specific font currently being used by the application does not contain a drawing (glyph) for that code point. Fixing this requires installing a comprehensive font family, such as Google's Noto (short for "No Tofu"), which provides glyphs for almost the entire Unicode standard.
Finally, a classic error in database administration involves the infamous MySQL utf8 character set. In the early 2000s, MySQL implemented a character set called utf8, but they made a critical architectural error: they hard-coded it to support a maximum of 3 bytes per character. This meant it could only store characters in the Basic Multilingual Plane. When smartphones popularized 4-byte emojis like the smiling poop (💩, U+1F4A9), applications using MySQL's utf8 began crashing violently upon saving user comments. To fix this, MySQL was forced to release a completely new character set named utf8mb4 (UTF-8 Max Bytes 4). Even today, countless developers mistakenly select utf8 instead of utf8mb4, severely limiting their application's capabilities.
Best Practices and Expert Strategies
The golden rule of modern software engineering, universally agreed upon by industry experts, is the "UTF-8 Everywhere" manifesto. This strategy dictates that text should be stored in memory, saved to disks, and transmitted over networks exclusively as UTF-8. You should never attempt to guess a file's encoding based on its contents; instead, you must explicitly declare the encoding at every boundary of your system. In web development, this means always including the <meta charset="utf-8"> tag in the <head> of your HTML documents, configuring your HTTP response headers to specify Content-Type: text/html; charset=utf-8, and ensuring your database connection strings explicitly request UTF-8 communication.
Another expert strategy involves mastering Unicode Normalization Forms. Because Unicode allows certain characters to be represented in multiple ways (the precomposed vs. decomposed "é" example mentioned earlier), comparing strings can yield false negatives. The Unicode standard defines four normalization forms, but the two most important are Normalization Form C (NFC) and Normalization Form D (NFD). NFC attempts to combine characters into single, precomposed code points wherever possible, while NFD breaks them apart into base characters and combining marks. The World Wide Web Consortium (W3C) strongly recommends using NFC for all web content. Before saving user input to a database, or before running a search query, a professional developer will always pass the string through an NFC normalization function to guarantee consistency.
When dealing with legacy systems or third-party APIs that do not support Unicode, experts employ strict fallback and sanitization strategies. Rather than allowing an application to crash when it encounters an unmappable character, developers use replacement characters. In Unicode, U+FFFD is the official "Replacement Character," rendered visually as a black diamond with a white question mark (). By configuring your parsers to gracefully swap invalid byte sequences with U+FFFD, you preserve the readability of the surrounding text while clearly signaling to the user and the logs that an encoding error occurred at that specific position.
Edge Cases, Limitations, and Pitfalls
Despite its brilliance, the Unicode standard contains several edge cases and vulnerabilities that developers must carefully navigate. One of the most notorious security pitfalls is the Homoglyph Attack. Because Unicode encompasses every language, it contains many characters that look visually identical to a human but have entirely different code points. For example, the standard Latin lowercase "a" is U+0061. However, the Cyrillic lowercase "а" is U+0430. A malicious actor can register a domain name like paypal.com, replacing the Latin "a" with the Cyrillic "а". To a user, the URL looks perfectly legitimate, but the browser will direct them to a completely different, fraudulent server. Modern browsers mitigate this by using a system called Punycode to display non-Latin URLs in a safe, ASCII-only format, but homoglyph attacks remain a threat in localized applications.
Another significant pitfall involves bidirectional text handling. Unicode supports languages that read Left-to-Right (LTR) like English, and languages that read Right-to-Left (RTL) like Arabic and Hebrew. To handle documents that mix both, Unicode includes invisible control characters like the Right-to-Left Override (U+202E). This character forces all subsequent text to be displayed backward. Hackers have exploited this by naming a malicious file evilsxe.doc, but inserting the RTL override character right before the "s". When the operating system displays the file name, it reverses the end of the string, making it appear as evildoc.exe. Security software and file upload validators must strictly strip or sanitize bidirectional control characters to prevent this spoofing.
The sheer size of the Unicode standard also presents limitations in string processing, particularly with regular expressions (Regex). A developer might write a simple Regex pattern like ^[a-zA-Z]+$ to validate that a user's name only contains alphabetical letters. However, this pattern only checks for ASCII letters. It will instantly reject valid names like "René", "Björn", or "O'Brien". To properly validate international names, developers must utilize advanced Unicode property escapes in their Regex, such as \p{L}, which matches any character categorized as a "Letter" across all 149,000+ Unicode code points. Failing to account for this limitation results in highly exclusionary software that frustrates global users.
Industry Standards and Benchmarks
The integration of Unicode into modern technology is governed by strict industry standards established by powerful consortiums. The Internet Engineering Task Force (IETF) codified UTF-8 as the standard encoding for the internet in RFC 3629, published in 2003. This document formally restricts the UTF-8 definition to a maximum of 4 bytes (stopping at U+10FFFF) to perfectly align with the limits of the UTF-16 architecture, ensuring seamless conversion between the two encodings. Any software that claims to be internet-compliant must adhere perfectly to the byte-sequence rules defined in RFC 3629.
The World Wide Web Consortium (W3C), which develops the standards for HTML and CSS, has made UTF-8 the absolute mandate for modern web development. In the HTML5 specification, the W3C explicitly states that authors should use UTF-8 and strictly forbids the use of certain legacy encodings that pose security risks. Web browsers are required to assume a document is UTF-8 if no other encoding is specified. As a result of these aggressive standardization efforts, benchmarks from technology survey groups like W3Techs show that over 98% of all websites on the internet currently use UTF-8.
In the realm of database management, industry benchmarks have shifted heavily toward maximum Unicode compliance. As mentioned previously, MySQL and MariaDB have deprecated their flawed 3-byte implementations and strongly recommend utf8mb4 with the collation utf8mb4_unicode_ci or utf8mb4_0900_ai_ci as the default benchmark for new databases. PostgreSQL has utilized UTF-8 as its default encoding for years. For programming languages, the benchmark for modern language design (such as Rust, Go, and Swift) is to treat all native string types as UTF-8 encoded byte arrays by default, forcing developers to interact with text in a globally safe manner right out of the box.
Comparisons with Alternatives
To appreciate the dominance of Unicode, one must compare it to the legacy alternatives it replaced. The most famous alternative is ASCII. As discussed, ASCII is a 7-bit system limited to 128 characters. The advantage of ASCII is its absolute minimal footprint; you can guarantee that every character will take exactly 1 byte, making memory allocation incredibly simple. However, its complete inability to support anything beyond basic American English makes it entirely obsolete for user-facing text in the 21st century. Today, ASCII is not truly an alternative, but rather a historical subset perfectly enveloped within the first 128 code points of Unicode.
Windows-1252 (often confused with ISO-8859-1) was the dominant 8-bit encoding alternative used by Microsoft during the 1990s and early 2000s. It could represent 256 characters, providing support for Western European languages, smart quotes, and the Euro symbol. The main benefit of Windows-1252 was that it was fixed-width (1 byte per character), making string processing faster than variable-width UTF-8. However, if you wanted to write a document containing both French and Russian, Windows-1252 was useless. You would have to switch the entire document's encoding to Windows-1251 (Cyrillic), losing the French accents. Unicode's ability to mix an infinite number of languages in a single document is why it utterly defeated the Windows code page system.
In Japan, a highly specific encoding called Shift JIS was the standard alternative. Because Japanese requires thousands of Kanji characters, a standard 8-bit system was insufficient. Shift JIS used a complex mix of 1-byte and 2-byte sequences to encode Japanese text alongside ASCII. While it was highly optimized for the Japanese language, it was notoriously difficult to parse programmatically. A software engineer parsing Shift JIS had to constantly check if a byte was a standalone character or the first half of a two-byte character. Furthermore, Shift JIS was completely incompatible with other Asian encodings like China's GB2312 or Korea's EUC-KR. Unicode replaced all of these regional standards by mapping every CJK (Chinese, Japanese, Korean) character into a single, unified mathematical space, allowing a single software build to be shipped globally without regional localization hacks.
Frequently Asked Questions
What is the difference between Unicode and UTF-8? Unicode is the theoretical map or dictionary; it assigns a unique numerical ID (code point) to every character, such as U+0041 for the letter "A". UTF-8 is the physical implementation of that map; it is the specific mathematical algorithm that dictates how to turn that abstract number into the binary zeros and ones (bytes) that are actually saved on a hard drive. You can think of Unicode as the alphabet itself, and UTF-8 as the specific font or handwriting used to write it down.
How do I type a specific Unicode character on my keyboard? Typing Unicode characters depends heavily on your operating system. On Windows, you can hold the "Alt" key, press the "+" key on the numeric keypad, type the hexadecimal code point (e.g., 00E9 for é), and release Alt. Alternatively, you can use the built-in Character Map application. On macOS, you can enable the "Unicode Hex Input" keyboard layout, hold the "Option" key, and type the four-digit hex code. For most everyday users, using copy-and-paste from a web-based Unicode lookup table is the fastest and most reliable method.
Why do some emojis show up as empty squares or boxes with an "X" inside? These empty boxes are known as "tofu" and they indicate a font rendering failure, not an encoding failure. The underlying data is perfectly intact, and your device successfully recognized the Unicode code point. However, the specific font package currently being used by your web browser or operating system has not been updated to include a drawing (glyph) for that specific code point. Updating your operating system or installing a comprehensive font like Google Noto will usually resolve the issue.
What is a Byte Order Mark (BOM) and should I use it? A Byte Order Mark (BOM) is a special Unicode character (U+FEFF) placed at the very beginning of a text file to signal to the reading program exactly which encoding (like UTF-16 or UTF-8) was used, and in what byte order (endianness) it was saved. While mandatory for UTF-16, the BOM is highly discouraged when using UTF-8. Because UTF-8 is byte-oriented, it has no endianness issues. Including a BOM in a UTF-8 file often causes errors in web development, such as breaking PHP scripts or causing visual glitches at the top of HTML pages.
How many characters can the Unicode standard actually hold? The Unicode standard architecture is strictly limited to exactly 1,114,112 code points, ranging from U+0000 to U+10FFFF. Currently, only about 149,000 of these spaces (roughly 13%) have been officially assigned to characters, symbols, and emojis. Furthermore, about 137,000 spaces are permanently reserved for private use, and a small block is reserved for surrogate pairs. This leaves hundreds of thousands of empty code points available, ensuring Unicode has more than enough space to accommodate any future symbols or historic scripts discovered by humanity.
Why does JavaScript count characters incorrectly when I use emojis?
JavaScript was created in the mid-1990s and was built around the UTF-16 encoding standard. When you use the .length property on a string in JavaScript, it does not count the number of visual characters; it counts the number of 16-bit code units. Because emojis reside in the higher Unicode planes, they require two 16-bit units (a surrogate pair) to be represented in UTF-16. Therefore, a single smiling emoji like "😀" will return a length of 2 in JavaScript. Modern developers must use advanced iteration methods like Array.from(string).length to count grapheme clusters accurately.