> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/js/val/prim/str/unicode/grapheme-cluster.md).

# grapheme cluster

[JS](/web/js.md) ⟩ [value](/web/js/val.md) ⟩ [primitive](/web/js/val/prim.md) ⟩ [String](/web/js/val/prim/str.md) ⟩ [Unicode](/web/js/val/prim/str/unicode.md) ⟩ grapheme cluster

{% hint style="success" %}
the <mark style="color:yellow;">**real characters**</mark>**/**<mark style="color:yellow;">**symbols**</mark>, as displayed on screen or paper, may be <mark style="color:yellow;">**composed**</mark> of <mark style="color:orange;">**one or more**</mark> [**code points**](/web/js/val/prim/str/unicode/code-point.md).
{% endhint %}

{% tabs %}
{% tab title="🗺️" %} <img src="https://2527454625-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MfvEFZnSBhKT6fJmus0%2Fuploads%2FeAPkVT4OexgWTMyevBZY%2Fcode.point.svg?alt=media&amp;token=c612329d-0a48-4cfc-9949-764991d69905" alt="" class="gitbook-drawing">
{% endtab %}

{% tab title="🧨" %}
{% hint style="danger" %}
'<mark style="color:purple;">**`/u`**</mark>' flag <mark style="color:red;">**doesn't**</mark>**&#x20;**<mark style="color:yellow;">**do well**</mark> with [**grapheme clusters**](/web/js/val/prim/str/unicode/grapheme-cluster.md):exclamation:

```javascript
/<.>/u.test("<🏳️‍🌈>"),                  // false❗️
/<....>/u.test("<🏳️‍🌈>"),               // true ⭐️
```

{% endhint %}
{% endtab %}

{% tab title="⭐️" %}
{% hint style="warning" %}
when working with <mark style="color:yellow;">**text**</mark>, it’s best to split at the boundaries of <mark style="color:purple;">**grapheme clusters**</mark>, <mark style="color:red;">**not**</mark> at the boundaries of <mark style="color:yellow;">**Unicode characters**</mark> ([**code points**](/web/js/val/prim/str/unicode/code-point.md)):exclamation:
{% endhint %}

{% hint style="info" %}
•   👉 [Grapheme Clusters](https://exploringjs.com/impatient-js/ch_unicode.html#grapheme-clusters-the-real-characters), 📗 [Atoms of Text](https://exploringjs.com/impatient-js/ch_strings.html#atoms-of-text)

•  when talking about the actual rendered image, the term glyph is used.

•  unless you have very specific requirements or are able to query the font, use an API that segments strings into grapheme clusters wherever you need to deal with the notion of “character”. 👉 [grapheme-splitter - GitHub](https://github.com/orling/grapheme-splitter) ⭐️&#x20;

•  there are only two languages which handle this well: Swift and Perl 6.
{% endhint %}
{% endtab %}

{% tab title="💈" %}

* replit ⟩ [/u flag](https://replit.com/@pegasusroe/regex-u-flag#index.js), require ⟩ [String extension](/web/js/val/prim/str/ext.md)

```javascript
// ⭐ import
const _String = require('./ext/String_ext.js');      // String extension

[
    '🍎'.codeUnits,     // [ 55356, 57166 ]     // surrogate pair (2 code units)
    '🍎'.codePoints,    // [ 127822 ]           // 1 code point

    '🏳️‍🌈'.codeUnits,     // [ 55356, 57331, 65039, 8205, 55356, 57096 ]
    '🏳️‍🌈'.codePoints,    // [ 127987, 65039, 8205, 127752 ]    // 4 code points❗️

    // --------------------------------------------
    // ❗️regex works on "code units", by default.
    // --------------------------------------------
    
    /🍎{3}/.test("🍎🍎🍎"),                      // false❗️
    // let 🍎 = ab (where a = 55356, b = 57166)
    // then /🍎{3}/ = /ab{3}/ = /abbb/❗️
    // which is not /🍎🍎🍎/ = /ababab/❗️

    /<.>/.test("<🍎>"),                           // false❗️
    // <🍎> = <ab>, which is not <.>

    // --------------------
    // ✅ enable /u flag
    // --------------------

    /<.>/u.test("<🍎>"),                          // true ⭐️

    // ❗️'/u' flag doesn't do well with grapheme clusters.
    /<.>/u.test("<🏳️‍🌈>"),                          // false❗️
    /<....>/u.test("<🏳️‍🌈>"),                       // true ⭐️

    // ⭐️ 搜尋「漢字」
    `Hello Привет 你好`.match(/\p{sc=Han}/gu),     // [ '你', '好' ]

    // ⭐️ Script
    /\p{Script=Greek}/u.test("α"),      // → true
    /\p{Script=Arabic}/u.test("α"),     // → false

    // ⭐️ Alphabetic
    /\p{Alphabetic}/u.test("α"),        // → true
    /\p{Alphabetic}/u.test("!"),        // → false
    /\p{Alphabetic}/u.test("漢"),       // → true

].forEach(x => console.log(x));
```

{% endtab %}

{% tab title="🛠" %}

* [grapheme-splitter](/web/js/lib/grapheme-splitter.md)
  {% endtab %}

{% tab title="📗" %}

* [ ] Eloquent JavaScript ⟩ [Strings & Character Codes](https://eloquentjavascript.net/05_higher_order.html#h_gQf5HZNGpM)
* [ ] ExploringJS ⟩ [Atoms of Text - code points, JS characters, grapheme clusters](https://exploringjs.com/impatient-js/ch_strings.html#atoms-of-text)
  {% endtab %}

{% tab title="📘" %}

* [UTF-16 characters, code points, grapheme clusters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_codepoints_and_grapheme_clusters)
* String.prototype ⟩&#x20;
  * [.charCodeAt()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt)
  * [.codePointAt()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)
* [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) ⟩&#x20;
  * [.fromCodePoint()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)
  * [.fromCharCode()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode)
    {% endtab %}
    {% endtabs %}
