C++
UTF-8
std::string
character encoding
string length

Getting the actual length of a UTF-8 encoded stdstring?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The phrase "actual length" is the source of most confusion around UTF-8 strings in C++. std::string::size() returns the number of bytes, not the number of Unicode code points and definitely not the number of user-perceived characters on screen.

What std::string::size() Really Means

A UTF-8 string is stored as a sequence of bytes. Because UTF-8 uses one to four bytes per code point, a single visible symbol may occupy several bytes.

That means this code measures storage length, not text length:

cpp
1#include <iostream>
2#include <string>
3
4int main() {
5    std::string text = "caf\xC3\xA9";
6    std::cout << text.size() << '\n'; // 5 bytes
7}

The string contains four code points, but the accented letter uses two bytes, so size() reports five.

Counting UTF-8 Code Points

If by "actual length" you mean the number of Unicode code points, you can count leading bytes in the UTF-8 sequence. Continuation bytes always begin with the bit pattern 10, so every non-continuation byte starts a new code point.

cpp
1#include <cstddef>
2#include <iostream>
3#include <string>
4
5std::size_t utf8_code_point_count(const std::string& text) {
6    std::size_t count = 0;
7
8    for (unsigned char byte : text) {
9        if ((byte & 0b1100'0000) != 0b1000'0000) {
10            ++count;
11        }
12    }
13
14    return count;
15}
16
17int main() {
18    std::string cafe = "caf\xC3\xA9";
19    std::string smile = "\xF0\x9F\x99\x82";
20
21    std::cout << utf8_code_point_count(cafe) << '\n';  // 4
22    std::cout << utf8_code_point_count(smile) << '\n'; // 1
23}

This approach is simple and fast, but it assumes the input is valid UTF-8. If the bytes are malformed, the count may be meaningless.

Code Points Are Not Always Visible Characters

Even code point count may not match what a user thinks of as the number of characters. Some visible characters are built from multiple code points. A common example is an accented letter formed by a base character plus a combining mark.

For example, one rendered glyph can be stored as:

  • a single precomposed code point
  • or a base letter followed by a combining accent

Those two forms may look identical on screen but produce different code point counts. So there are really three different measurements:

  • bytes in memory
  • Unicode code points
  • grapheme clusters, meaning user-perceived characters

If your UI needs cursor movement, truncation, or text layout, code point count is often still not enough.

Validating Before Counting

When data comes from files, APIs, or user input, do not assume the bytes are valid UTF-8. A more robust implementation validates each sequence first.

cpp
1#include <cstddef>
2#include <iostream>
3#include <stdexcept>
4#include <string>
5
6std::size_t utf8_code_point_count_checked(const std::string& text) {
7    std::size_t count = 0;
8
9    for (std::size_t i = 0; i < text.size();) {
10        unsigned char byte = static_cast<unsigned char>(text[i]);
11        std::size_t width = 0;
12
13        if ((byte & 0b1000'0000) == 0) width = 1;
14        else if ((byte & 0b1110'0000) == 0b1100'0000) width = 2;
15        else if ((byte & 0b1111'0000) == 0b1110'0000) width = 3;
16        else if ((byte & 0b1111'1000) == 0b1111'0000) width = 4;
17        else throw std::runtime_error("Invalid UTF-8 leading byte");
18
19        if (i + width > text.size()) {
20            throw std::runtime_error("Truncated UTF-8 sequence");
21        }
22
23        for (std::size_t j = 1; j < width; ++j) {
24            unsigned char next = static_cast<unsigned char>(text[i + j]);
25            if ((next & 0b1100'0000) != 0b1000'0000) {
26                throw std::runtime_error("Invalid UTF-8 continuation byte");
27            }
28        }
29
30        ++count;
31        i += width;
32    }
33
34    return count;
35}

This still counts code points, but it fails fast on broken byte sequences instead of returning a misleading answer.

When You Need Grapheme Clusters

If you need to count what users visually perceive as characters, use a Unicode-aware text library such as ICU. Standard C++ does not provide a full grapheme cluster API, and trying to implement Unicode segmentation rules yourself is not realistic for most applications.

That distinction matters for:

  • text editors
  • terminal rendering
  • UI truncation with ellipses
  • cursor movement
  • input validation based on displayed length

In those cases, "actual length" should usually mean grapheme clusters, not bytes and not raw code points.

Common Pitfalls

  • Treating std::string::size() as a character count. It is a byte count.
  • Assuming code points and visible characters are always the same thing.
  • Counting non-continuation bytes without validating the input first.
  • Switching to std::wstring and assuming the problem disappears. Wide strings are platform-dependent and still do not solve Unicode text segmentation.
  • Forgetting to define what "length" means before writing the code.

Summary

  • 'std::string::size() reports bytes, not Unicode characters.'
  • Counting non-continuation bytes gives a code point count for valid UTF-8.
  • Invalid UTF-8 should be validated before counting.
  • User-visible character count requires grapheme cluster handling, usually via a library like ICU.
  • The correct solution depends on whether you need bytes, code points, or displayed characters.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.