Технічно технічний блог

Деякі експеременти, щось з програмування, щось із пізнання та дійсност

22 Jan 2022

Три символа у рядку, а довжина рядка - 4. Дивина, JavaScript, та й годі

published: January 22, 2022, updated: January 25, 2022; 17:01

По визначенню, JavaScript об’єкт String - текстовий рядок - (і в принципі це відноситься й до primitive) - набір букв/симовлів, які вираженні (пердставленні) за допомогою UTF-16. Кожен елемент в рядку займає свою позицію, місце, ідекс, який починається з 0. Довжина текстового рядка - це кількість букв в ньому. Напр, текст.рядок “ПТН ПНХ” містить 7 символів, його довжина (length) = 7, починаючи з індексу 0 (П) й закінчуючи індексом 6 (Х).

В ютф-16 символи (грубо кажучи букви, знаки, символи) кодуються за допомогою одного чи двох 16-бітних кодів (одиниць). В текстовому об’єкті джаваскріпту є властивість length, яка містить довжину тексту (текстового об’єкту). Здавалось б, що усе зрозуміло й доступно. Втім, ця властивість відображає довжину (кількість) текстового об' єкту, вимірюючи кількість UTF 16-бітних кодів. Якщо буква, символ, знак формується (кодується) за допомогою двох, а не одного, 16-бітних кодів, тоді довжина тексту, вказана у властивості length, не відповідатиме реальній кількості символів.

Наприклад, візьмемо ось цей ієрогліф (показано як картинку)

Джаваскріпт закодує його двома 16-бітними кодами ’\uD87E\uDC04’. Тоді

1
"A\uD87E\uDC04B".length == 4

Це тому, що length ‘порахує’ кожну кодову одиницю \uD87E і \uDC04 окремо, а також порахує букви A і B, які закодовані по одному 16-бітному коду, тобто в цій “довжині” 4 окремих 16-бітних коди, але це лише 3 символи (букви).

В документації MDN наводиться такий приклад функції як це можна обійти

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function getCharacterLength (str) {
  // The string iterator that is used here iterates over characters,
  //  not mere code units
  return [...str].length;
}

console.log(getCharacterLength('A\uD87E\uDC04Z')); // 3

// While not recommended, you could add this to each string as follows:

Object.defineProperty(String.prototype, 'charLength', {
  get () {
    return getCharacterLength(this);
  }
});

console.log('A\uD87E\uDC04Z'.charLength); // 3
Next time, we'll talk about "10 Reasons why gcc SHOULD be re-written in JavaScript - You won't believe #8!"