В процессе разработки я иногда сталкивался с проблемами, связанными с кодировкой, Unicode и Emoji, и обнаружил, что не полностью усвоил базовые знания в этой области. Поэтому после некоторого поиска и изучения я организовал несколько простых для понимания статей, чтобы поделиться ими.
Несколько статей из серии [Hardcore Foundation], о которых заинтересованные друзья могут узнать:
- Жесткая базовая двоичная (двух) битовая операция
- Основной базовый двоичный код (1) 0,1 + 0,2 != 0,3 и стандарт IEEE-754
- Хардкор Базовое кодирование (1) Горячее Горячее Горячее Горячее Горячее
- Hardcore Basic Coding (2) ascii, unicode и utf-8
Я не знаю, сталкивались ли вы с такими сомнениями, в требовании длины проверки формы вы найдете разные символыlength
Может различаться по размеру. Например, длина «𠮷» в названии равна 2 (обратите внимание, что 📢 — это не китайский иероглиф!).
'吉'.length
// 1
'𠮷'.length
// 2
'❤'.length
// 1
'💩'.length
// 2
Чтобы объяснить эту проблему, начнем с кодировки UTF-16.
UTF-16
отECMAScript® 2015Как видно из спецификации, строки ECMAScript кодируются в UTF-16.
Фиксированные и неопределенные:Наименьшая кодовая единица в UTF-16 составляет два байта, что является фиксированным, даже если первый байт может быть равен 0. Может случиться так, что для символов базовой плоскости (BMP) требуется всего два байта, указывающих диапазон
U+0000 ~ U+FFFF
, а для дополнительной плоскости требуется четыре байтаU+010000~U+10FFFF
.
В предыдущей статье мы представили детали кодировки utf-8 и узнали, что кодировка utf-8 занимает от 1 до 4 байтов, а для использования utf-16 требуется 2 или 4 байта. Давайте посмотрим, как кодируется utf-16.
Логика кодирования UTF-16
Кодировка UTF-16 очень проста для данной кодовой точки Unicode cp (CodePoint — это уникальный номер этого символа в Unicode):
- Если кодовая точка меньше или равна
U+FFFF
(то есть все символы базовой плоскости), не нужно обрабатывать, использовать напрямую. - В противном случае он будет разделен на две части.
((cp – 65536) / 1024) + 0xD800
,((cp – 65536) % 1024) + 0xDC00
хранить.
Стандарт Unicode указывает, что значение U+D800...U+DFFF не соответствует ни одному символу, поэтому его можно использовать для разметки.
В качестве конкретного примера: кодовая точка символа AU+0041
, который может быть непосредственно представлен символом.
'\u0041'
// -> A
A === '\u0041'
// -> true
в Javascript\u
Экранирующий символ, представляющий Юникод, за которым следует шестнадцатеричное число.
И кодовая точка символа 💩U+1f4a9
, символы в дополнительной плоскости, два символа получаются по формуле 👆55357
, 56489
Два числа представлены в шестнадцатеричном виде какd83d
, dca9
, который объединяет два результата кодирования в суррогатную пару.
'\ud83d\udca9'
// -> '💩'
'💩' === '\ud83d\udca9'
// -> true
Поскольку строки Javascript используютutf-16
кодирование, поэтому суррогат может быть правильно\ud83d\udca9
декодировать, чтобы получить кодовую точкуU+1f4a9
.
также можно использовать\u
+ {}
, за фигурными скобками непосредственно следует кодовая точка для представления символа. Они выглядят по-разному, но выражают один и тот же результат.
'\u0041' === '\u{41}'
// -> true
'\ud83d\udca9' === '\u{1f4a9}'
// -> true
Вы можете открыть панель консоли Dev Tool и запустить код, чтобы проверить результаты.
Так почему же возникает проблема с оценкой длины?
Чтобы ответить на этот вопрос, вы можете продолжить просмотрСпецификация, в котором упоминается: когда операции ECMAScript интерпретируют строковые значения, каждыйэлементинтерпретируются какне замужемКодовая единица UTF-16.
Where ECMAScript operations interpret String values, each element is interpreted as a single UTF-16 code unit.
Таким образом, такие символы, как 💩, на самом деле занимают две кодовые единицы UTF-16, то есть два элемента.length
собственность2
. (Это то же самое, что использовать JS в началеUSC-2
Связано с кодированием, я думал65536
характера хватает на все нужды)
Но для обычных пользователей это совершенно непонятно.Почему заполняется только одна '𠮷', а программа подсказывает, что длина в два символа занята. Как правильно определить длину символов Юникода?
я здесьAntd
Форма, используемая формойasync-validator
Следующий абзац можно увидеть в упаковкекод
const spRegexp = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g;
if (str) {
val = value.replace(spRegexp, '_').length;
}
Когда необходимо оценить длину строки, все символы в диапазоне кодовых точек в дополнительной плоскости будут заменены символами подчеркивания, чтобы оценка длины соответствовала фактическому отображению! ! !
Поддержка Юникода в ES6.
length
Проблема атрибутов в основном в том, что когда язык JS изначально разрабатывался, он не учитывал, что будет так много символов, и думал, что двух байт будет достаточно. Так что не толькоlength
, некоторые общие операции над строками находятся вUnicode
Поддержка также покажетаномальный.
Следующий контент представит некоторые ненормальныеAPI
И вES6
как правильно решать эти вопросы.
for vs for of
Например, используйтеfor
Выведите строку в цикле, строка будет проходиться по каждому «элементу», понятному JS, а символы вспомогательной плоскости будут распознаваться как два «элемента», поэтому появляются «искаженные символы».
var str = '👻yo𠮷'
for (var i = 0; i < str.length; i ++) {
console.log(str[i])
}
// -> �
// -> �
// -> y
// -> o
// -> �
// -> �
при использовании ES6for of
Грамматика не делает.
var str = '👻yo𠮷'
for (const char of str) {
console.log(char)
}
// -> 👻
// -> y
// -> o
// -> 𠮷
Синтаксис спреда
Как упоминалось ранее, использование регулярных выражений для замены символов вспомогательной плоскости для подсчета длины символов. Тот же эффект может быть достигнут с использованием синтаксиса расширения.
[...'💩'].length
// -> 1
Та же проблема существует с методами slice, split, substr и т.д.
регулярное выражение ты
ES6 также добавляет новую функцию для символов Unicode.u
Дескриптор.
/^.$/.test('👻')
// -> false
/^.$/u.test('👻')
// -> true
charCodeAt/codePointAt
Для строк мы также используемcharCodeAt
Чтобы получить Code Point, это применимо к символам плоскости BMP, но если символы являются вспомогательными символами плоскостиcharCodeAt
Возвращаемым результатом будет только номер первой пары символов после кодирования.
'羽'.charCodeAt(0)
// -> 32701
'羽'.codePointAt(0)
// -> 32701
'😸'.charCodeAt(0)
// -> 55357
'😸'.codePointAt(0)
// -> 128568
при использованииcodePointAt
тогда символ распознается правильно и возвращается правильная кодовая точка.
String.prototype.normalize()
Поскольку в JS под строкой понимается последовательность двухбайтовых элементов кода, о равенстве судят по значению последовательности. Таким образом, могут быть некоторые строки, которые выглядят точно так же, но результат решения о равенстве строк действительноfalse
.
'café' === 'café'
// -> false
Первый в приведенном выше кодеcafé
Там естьcafe
плюс фонетический символ с отступом\u0301
составлено, а второеcafé
поcaf
+ é
состоит из символов. Таким образом, хотя они выглядят одинаково, но кодовые точки разные, поэтому результат оценки равенства JS равенfalse
.
'cafe\u0301'
// -> 'café'
'cafe\u0301'.length
// -> 5
'café'.length
// -> 4
Чтобы правильно идентифицировать этот тип оценки строки с разными кодовыми точками, но с той же семантикой, ES6 добавилString.prototype.normalize
метод.
'cafe\u0301'.normalize() === 'café'.normalize()
// -> true
'cafe\u0301'.normalize().length
// -> 4
Суммировать
Эта статья в основном представляет собой мои недавние учебные заметки для повторного изучения кодирования. Из-за спешки времени и ограниченного уровня в статье должно быть много неточных описаний и даже неправильного содержания. Если вы что-то найдете, пожалуйста, укажите на это . ❤️