Skip to content
← Back to Blog
String LengthUnicodeDeveloper ToolsString Tools

String Length: Characters vs Bytes vs Graphemes

Ask three tools how long a string is and you can get three different answers — and all three can be correct. One counts what you see, one counts storage, and one counts the invisible plumbing underneath. For plain English text they agree, which is why the difference only shows up at the worst moment: an emoji-filled bio rejected by the database, or one accent that triples your SMS bill. You can count characters and bytes in the browser, or read on for why the numbers disagree.

RMBG.PRO string length counter tool

Published on September 20, 2026

There are three common answers to “how long is this string”, and they measure different things. Bytes measure storage: how many 8-bit units the text occupies in a given encoding. Code points measure Unicode values: how many numbered entries the text uses. Graphemes measure perception: how many characters a human reader sees.

Take the word café. A reader sees 4 graphemes. In its composed form it is 4 code points, but in UTF-8 it occupies 5 bytes, because é needs 2 bytes. Take a flag emoji: 1 grapheme the user sees, 2 code points under the hood, 8 bytes in UTF-8, and 4 UTF-16 code units. Same string, four numbers.

Two facts explain nearly every length-related bug. First, naive counters pick one unit and pretend it is all three — JavaScript's .length counts UTF-16 units, C's strlen counts bytes, and neither is “the” length. Second, limits pick their own unit — a database column counts characters, an API payload counts bytes, an SMS counts encoded symbols — and the bug is always a mismatch between the counter and the limit.

The rest of this article covers the three units in detail, why emoji and accents break naive counts, the UTF-8 vs UTF-16 maths, the practical limits that actually reject your text, when to validate by characters and when by bytes, and what changes with Arabic and bidirectional text. If you only need the numbers, the string length tool reports characters, words and bytes side by side. It is part of the wider string tools set alongside the word counter.

Characters, Bytes and Graphemes — With Examples

Applied to the same strings, here is what each unit reports and which tool or limit uses it:

StringGraphemes (seen)UTF-8 bytesJS .length (UTF-16 units)
hello555 — all three agree on ASCII
café454
😀142 (surrogate pair)
e + ́ (decomposed é)132
family emoji 👨‍👩‍👧118+8+ (joiner sequence)

The pattern: ASCII text makes every counter agree, which is why the differences hide until real-world text — accents, emoji, non-Latin scripts — arrives. Test your counter with café 😀 and you learn immediately which unit it reports.

Why Emoji and Accents Break Naive Counts

Two mechanisms cause almost all surprises. The first is multi-byte characters: in UTF-8, ASCII letters take 1 byte, accented Latin letters take 2, most non-Latin scripts take 2–3, and emoji take 4. Any counter that reports bytes therefore grows faster than the visible text the moment you leave plain English.

The second is combining sequences: characters built from multiple code points that render as one glyph. An é can be one precomposed code point or the letter e plus a combining accent — identical on screen, different counts. Emoji are worse: flags are pairs of regional indicators, and family or profession emoji are whole chains joined by zero-width joiners. A single visible “character” can be eight code points long.

  • JavaScript .length counts UTF-16 code units. Anything outside the Basic Multilingual Plane — nearly all emoji — counts as 2. Use Array.from(str).length for code points or Intl.Segmenter for graphemes.
  • Python len() counts code points. Better, but still not graphemes: a flag emoji reports 2, a ZWJ family reports 7+. Character counts and visible characters still differ.
  • Normalization changes the count. NFC (composed) and NFD (decomposed) forms of the same word have different code point counts. Two databases storing the same name in different normal forms disagree on its length.

The fix is not a better single number — it is reporting the right number for the job, which is why the string length tool shows characters, words and UTF-8 bytes together instead of pretending one of them is “the” length.

UTF-8 vs UTF-16 Byte Maths

Storage encodings turn the same code points into different byte counts. UTF-8 uses 1 byte for ASCII, 2 for accented Latin, Greek, Cyrillic, Hebrew and Arabic, 3 for most Asian scripts, and 4 for emoji and rare scripts. UTF-16 uses 2 bytes for everything in the Basic Multilingual Plane and 4 bytes (a surrogate pair) for everything outside it.

The practical consequence: a French product description is roughly the same size in both encodings, an Arabic catalog is roughly the same too, but an emoji-heavy social bio is larger in UTF-8 (4 bytes per emoji) than the JavaScript .length suggests (2 units per emoji). Neither number is wrong; they answer different questions.

For developers, the conversions are one-liners: new TextEncoder().encode(str).length gives UTF-8 bytes in the browser, len(s.encode("utf-8")) in Python, and Buffer.byteLength(str, "utf8") in Node.js. When an API documents a byte cap, this is the number to check — not the number in the input field's character counter.

The Limits That Actually Reject Your Text

Length only matters because something enforces it. The four limits that bite most often:

  • Database columns. A VARCHAR(255) limit is usually 255 characters, but byte-limited types and older MySQL utf8 (3-byte) columns reject 4-byte emoji entirely. The error appears on submit, long after the user passed frontend validation.
  • SMS segmentation. 160 GSM-7 characters per segment, or 70 in UCS-2 the moment one out-of-alphabet character appears — then 153/67 per concatenated segment after the first. A single emoji can flip a 1-segment message into 3.
  • API field caps. Many APIs cap fields in bytes (payload limits) while documenting them in characters. A username field documented as “32 characters” may reject 32 emoji because they exceed the byte budget underneath.
  • Form validation. Frontend maxlength counts UTF-16 units in most browsers — so it agrees with JavaScript .length but not with the backend, the database, or the user's eyes.

The rule: find out which unit the enforcing layer counts, and validate in that unit. When the docs are silent, test with emoji — they separate character counters from byte counters faster than any documentation.

When to Validate by Characters, When by Bytes

Validate by graphemes or code points when the limit is about human content: display names, bios, titles, comments, meta descriptions. The user thinks in visible characters, so the counter should too.

Validate by bytes when the limit is about transport or storage: request payload caps, database byte-limited columns, webhook size limits, encoded URL length. Here the visible length is irrelevant — what matters is what crosses the wire.

And always validate on both sides in the same unit. A frontend counting graphemes while the backend counts bytes is the single most common shape of this bug, and it only ever appears with non-ASCII input — which is exactly the input your test suite probably lacks.

Arabic Text and Bidirectional Characters

Arabic adds two wrinkles. First, diacritics (harakat): the same visible word can be stored with or without vowel marks, and with precomposed or decomposed forms, so two “identical” strings report different lengths. Normalizing to NFC before counting removes most of the disagreement.

Second, directionality: mixed Arabic-and-Latin text contains invisible bidirectional control characters that affect rendering order. They are invisible on screen but count in every unit — bytes, code points, UTF-16 length, all of them. A copied product title that “looks” 30 characters long can be 34 because of two hidden marks.

Arabic script itself has no upper/lower case, so case converters are no-ops on it — but length counters treat it like any other non-ASCII script: 2 bytes per letter in UTF-8, 1 UTF-16 unit per letter. Pasted Arabic text that fails a limit usually fails on bytes, not on visible length.

Count It Without the Guesswork

Whatever limit you are checking against, the counting itself should be one paste away:

  • A free string length counter reporting characters, words and UTF-8 bytes side by side.
  • Runs entirely in your browser — nothing you paste is uploaded.
  • Pairs with the word counter when you need words and reading time too.
  • Part of the wider string tools set: word counter, case converter, URL encoder and Base64 encoder.

Try the String Length Tool

Paste your text and see characters, words and bytes instantly, in your browser.

Count Characters

Frequently Asked Questions

What is the difference between characters, bytes and graphemes?▾

A byte is a unit of storage, a code point is a numbered Unicode value, and a grapheme is what a human perceives as one character. The word "cafe" is 4 graphemes, 4 code points in NFC form, but 5 bytes in UTF-8 because e-acute takes 2 bytes. An emoji flag can be 1 grapheme built from 2 code points and 8 bytes. Which number you want depends on the question: display width needs graphemes, storage and API limits need bytes, and most form validation needs graphemes or code points.

Why does JavaScript .length give the wrong answer for emoji?▾

Because JavaScript strings are UTF-16 code units, and .length counts those units, not characters. Most emoji live outside the Basic Multilingual Plane, so each one takes two UTF-16 units (a surrogate pair) and .length reports 2 for a single visible emoji. Family emoji and flags built with zero-width joiners report even higher numbers. Use Array.from(str).length for code points, or Intl.Segmenter with granularity grapheme for what the user actually sees.

Should I validate form input by characters or by bytes?▾

Validate the same unit the backend enforces. If the database column is VARCHAR(255), that limit is usually in characters, so count graphemes or code points on the frontend. If the column is a byte-limited type, or the API documents a byte cap, count UTF-8 bytes instead. The classic bug is a frontend that counts characters while the backend counts bytes: a bio full of emoji passes validation in the browser and then fails with a database error on submit.

How many characters are in an SMS segment?▾

A single SMS segment holds 160 characters in the GSM-7 alphabet (basic Latin, digits and a few symbols) or 70 characters in UCS-2 encoding, which is used as soon as the message contains any character outside GSM-7, such as an accent-heavy French text, Arabic script or emoji. Longer messages are split into concatenated segments of 153 GSM-7 or 67 UCS-2 characters each. One emoji in an otherwise plain message can switch the whole message to UCS-2 and triple the segment count.

Why does the same Arabic text have different lengths in different tools?▾

Usually because of normalization and what each tool counts. Arabic letters with diacritics (harakat) can be stored as precomposed or decomposed sequences, so the same visible word has different code point counts. Some tools count UTF-16 code units, others count code points, others count graphemes, and a bidirectional control character invisible on screen still counts everywhere. Compare tools by checking whether they normalize input first and which unit they report.

How do I count string length correctly?▾

Decide which unit your limit uses, then use the matching method. For what the user sees, use grapheme segmentation (Intl.Segmenter in the browser). For storage or API byte caps, encode to UTF-8 and count the bytes (TextEncoder in JavaScript, len(s.encode utf-8) in Python). For a quick check of any of the three, paste the text into the RMBG.PRO string length tool, which reports characters, words and UTF-8 bytes side by side without uploading anything.

Similar articles

View all