Counting the Number of Characters

Challenge

Create a program that reads a string, then echoes back the string together with its length.

Background

We already know how to read a string from user input from the previous challenge. The remaining question is how to calculate its length. Intuitively, the length of a string is the number of characters that it has. But what does it mean by a character?

The lengths of ASCII strings

For ASCII strings, each character is represented by a 1-byte code point. The length of a string is the same as the number of bytes used to store the string.

The lengths of UTF-8 strings

For Unicode strings, the answer is more complex. Consider the emoji '🤦🏼‍♂️'. It is one graphical unit represented by 17 bytes:1

Scalar namePrinted scalarCode units
U+1F926 Face Palm🤦4
U+1F3FC Emoji Modifier Fitzpatrick Type-3🏼4
U+200D Zero Width Joinernot printable3
U+2642 Male Sign3
U+FE0F Variation Selector-16◌️3
Total: 17

Here we are presented with several terms:

  • UTF-8 code unit: 8 bits/1 byte
  • Unicode code point: A position in the Unicode character set. In the UTF-8 encoding, each code point is represented by one to four code units.
  • Unicode scalar value: A Unicode code point except high-surrogate and low-surrogate code points. In other words, the set of scalar values is a subset of code points.
  • Extended grapheme cluster: The whole emoji is an extended grapheme cluster.

Therefore, when it comes to a UTF-8 string, we have multiple lengths measurements. For the string "🤦🏼‍♂️":

  • The number of bytes used to encode it: 17,
  • The number of Unicode scalar values/code points: 5,
  • The number of extended grapheme clusters: 1.

Solution

To count the number of code units and scalar values, we can use the built-in functions str::len() and str::chars(). For the number of extended grapheme clusters, we shall use the crate unic_segment instead of implementing our own segmentation algorithm.

    let num_bytes = input.len();
    let num_scalars = input.chars().count();
    let num_egc = unic_segment::Graphemes::new(&input).count();

Example output:

Enter a string:
🤦🏼‍♂️🤦🏼
Your string '🤦🏼‍♂️🤦🏼' has:
        25 byte(s)
        7 scalar value(s)
        2 extended grapheme cluster(s).

1 More at It's Not Wrong that "🤦🏼‍♂️".length == 7, Henri Sivonen.