Понимание UTF в Javascript

Обзор

  • Javascript использует кодировку UTF-16 для строки.
  • Строка формирования символов может быть из BMP или не BMP.
  • Длина строки — это не что иное, как количество единиц 16-битного блока памяти. Например, если длина строки равна 4, это означает, что она имеет 4 единицы 16-битных блоков памяти.
  • Каждый символ BMP может быть представлен с использованием одной единицы 16-битного блока памяти.
  • Каждый символ, отличный от BMP, может быть представлен двумя единицами 16-битных блоков памяти (т. е. 32 бита или 4 октета). Например, японские символы или символы эмодзи могут иметь размер 2 единицы.
  • С математической точки зрения размер или длина строки в Javascript имеет вид x + y, x — это общее количество символов (включая символы BMP и не BMP), а y — общее количество символов, отличных от BMP (y равно 0, если строка содержит только символы BMP). символы). То есть для каждого символа, отличного от BMP, добавляется одна дополнительная единица 16-битного блока памяти к общему размеру или длине этой строки.

Иллюстрация

Рассмотрим строку «Hello😛». Теперь мы получаем 7 как длину этой строки:

'Hello😛'.length; // 7

Здесь 'H', 'e', 'l', 'l', 'o', '😛' составляют 6 единиц размера (или длины) всей строки. Поскольку '😛' не является BMP-символом, он даст 1 дополнительную единицу. Другими словами, строка содержит 1 символ, отличный от BMP, что приводит к тому, что общий размер приведенной выше строки составляет 6 + 1 = 7. Таким образом, связав это с математической формулой x + y, мы можем написать x = 6(общее количество символов) и y = 1(количество не BMP-символов '😛').

Подразумеваемое

Мы видели, что строка в Javascript имеет форму x + y. Также строка в Javascript - это не что иное, как массив блоков памяти, а не символы. Другими словами, конкретный блок памяти в строке s в определенной позиции индекса i может быть извлечен с использованием s[i]. Самое большое заблуждение среди людей состоит в том, что s[i] означает символ в позиции i в строке s. Это верно только для такой строки, которая содержит только символы BMP. В тот момент, когда в строке появляется не-BMP-символ, положение всех последующих символов изменяется из-за того, что не-BMP-символ занимает более 2 единиц блока памяти. Это очень легко понять на следующем примере:

Рассмотрим строку s ‘Hello😛World’. Итак, когда мы обращаемся к строке s единица за единицей блока памяти до символа «o», мы получаем:

s[0]  'H'
s[1]  'e'
s[2]  'l'
s[3]  'l'
s[4]  'o'

Как вы думаете, каким будет значение s[5]? Что ж, мы получим '�', так как значение блока памяти s[5] не сопоставляется ни с одним печатным/визуализируемым символом. «�» — это просто заполнитель, выбранный браузером, чтобы показать, что он не может отображать такой символ. То же самое верно для s[6], который показывает '�'. Мы можем получить десятичный эквивалент s[5] и s[6] или данных, хранящихся в блоке памяти s[5] и s[6], как показано ниже:

s[5] // '�'
s[6] // '�'
s[5].charCodeAt(0) // 55357
s[6].charCodeAt(0) // 56859

Записав значение блока памяти в s[5] и s[6] в шестнадцатеричном формате, мы получим:

s[5] // 0xD83D
s[6] // 0xDE1B

Итак, вместе s[5] и s[6] представляют 😛. То есть пара значений данных 0xD83D и 0xDE1B вместе представляют один не-BMP-символ 😛. Эта пара данных (пара 16-битных значений) называется суррогатными парами.

Проблемы

Выше мы видели, что для каждого символа, отличного от BMP, требуется 2 единицы блоков памяти (суррогатная пара), что искажает обычную итерацию по символам строки в Javascript. Это препятствует рассмотрению строки как массива символов для числовых целей, таких как подсчет частоты символов в строке, замена символа в строке, добавление символов в строку и т. д. Короче говоря, любой вид вычисления, связанного со строкой, с точки зрения символ становится ошибочным из-за наличия в строке символа, отличного от BMP. К счастью, у нас есть обходной путь для этой проблемы. Далее мы увидим, как этот обходной путь работает.

Обходной путь

Для любой практической цели рассмотрения строки как массива нам сначала нужно разбить строку на массив символов (обоих типов: BMP и не-BMP). То есть этот массив будет иметь точно такое же количество элементов, как и количество символов (учтите, что это не длина или размер строки!). Рассматривая ту же строку примера 'Hello😛World' еще раз, мы можем преобразовать ее в массив символов, как показано ниже:

const [...ca] = 'Hello😛World';
ca // (11) ["H", "e", "l", "l", "o", "😛", "W", "o", "r", "l", "d"]

Как и в Javascript, элементы массива не обязательно должны быть однородными, и каждый элемент может относиться к любому типу данных, а также к любому размеру данных. Таким образом, приведенный выше массив символов ca, состоит из 11 элементов, каждый из которых представляет символ (как BMP, так и не-BMP без проблем). Это то, что мы хотели для нашей обработки на основе строк. Например, если мы хотим подсчитать частоты символов в приведенной выше строке, мы можем получить это, как показано ниже:

const [...ca] = 'Hello😛World';
ca.reduce((obj, v) =>
	{
		obj[v] = obj[v] || 0;
		++obj[v];
		return obj;
	}, {});
H: 1
e: 1
l: 3
o: 2
😛: 1
W: 1
r: 1
d: 1

Вывод

  • Строка в Javascript состоит из символов UTF-16, каждый из которых имеет размер 1 (в случае символа BMP) или 2 единиц (в случае символов, отличных от BMP) 16-битных блоков памяти.
  • Строка в javascript - это не массив символов, а массив блоков памяти, каждый из которых имеет размер 16 бит.
  • Каждый символ, отличный от BMP, занимает более 2 единиц 16-битной памяти, тем самым увеличивая длину строки на одну дополнительную единицу на символ.
  • Длину или размер строки javascript можно математически представить как x+y, где x — общее количество символов, а y — общее количество символов, отличных от BMP.
  • Оператор распространения ES6 можно использовать для преобразования строки в массив символов.