Как найти размер текстового сообщения

Определить объём текста

Онлайн калькулятор легко и непринужденно вычислит объем текста в битах, байтах и килобайтах. Для перевода в другие единицы измерения данных воспользуйтесь онлайн конвертером.

Информационный вес (объем) символа текста определяется для следующих кодировок:
Unicode UTF-8
Unicode UTF-16
ASCII, ANSI, Windows-1251

Текст

Символов 0

Символов без учета пробелов 0

Уникальных символов 0

Слов 0

Слов (буквенных) 0

Уникальных слов 0

Строк 0

Абзацев 0

Предложений 0

Средняя длина слова 0

Время чтения 0 сек

Букв 0

Русских букв 0

Латинских букв 0

Гласных букв 0

Согласных букв 0

Слогов 0

Цифр 0

Чисел 0

Пробелов 0

Остальных знаков 0

Знаков препинания 0

Объем текста (Unicode UTF-8) бит 0

Объем текста (Unicode UTF-8) байт 0

Объем текста (Unicode UTF-8) килобайт 0

Объем текста (Unicode UTF-16) бит 0

Объем текста (Unicode UTF-16) байт 0

Объем текста (Unicode UTF-16) килобайт 0

Объем текста (ASCII, ANSI, Windows-1251) бит 0

Объем текста (ASCII, ANSI, Windows-1251) байт 0

Объем текста (ASCII, ANSI, Windows-1251) килобайт 0

Почему на windows сохраняя текст блокноте перенос строки занимает — 4 байта в юникоде или 2 байта в анси?
Это историческое явление, которое берёт начало с дос, последовательность OD OA (nr ) в виндовс используются чтоб был единообразный вывод на терминал независимо консоль это или принтер. Но для вывода просто на консоль достаточно только n.

В юникоде есть символы которые весят 4 байта, например эмоджи: 🙃

×

Пожалуйста напишите с чем связна такая низкая оценка:

×

Для установки калькулятора на iPhone — просто добавьте страницу
«На главный экран»

Для установки калькулятора на Android — просто добавьте страницу
«На главный экран»

При работе с текстовыми данными нередко возникает необходимость определить объём текста с пробелами или без. Отмечать нажатия клавиш или подсчитывать буквы вручную глупо и бессмысленно и не только потому, что текст может содержать несколько тысяч символов. Для подобной рутинной работы есть программы, а вернее встроенные в программы функции, с некоторыми из которых мы сегодня познакомимся.

Удобнее всего считать символы в Word. Если вы посмотрите в левый нижний угол окна редактора, то увидите там маленькую панельку «Число слов». Если по ней кликнуть, откроется окошко статистики, в котором будет указано длина текста с пробелами и без, а также число строк, абзацев, слов, а если документ многостраничный, то и страниц. Аналогичным способом в Word можно определять количество символов в выделенном тексте.

Символы в Word

Ничуть не хуже с подсчётом символов справляются специальные онлайновые сервисы. Достаточно вбить в поиск Google запрос «длина символов онлайн» и вы получите несколько десятков подобных ресурсов. Нередко обладая расширенным функционалом, они позволяют не только определять длину текста, но и проводить предварительную обработку текста, например, вырезать теги, удалять дубликаты, игнорировать определённые символы и т.д.

Длина символов онлайн

Однако и Word, и интернет-сервисы являются сторонними средствами, которые не всегда могут быть доступны. А как быть, если вы работаете в «голой» Windows и к тому же без подключения к интернету? Ну что же, определить длину текста можно и средствами одной Windows. Мало кто из пользователей знает, что функцию подсчёта символов имеет самый обычный Блокнот. Находится она в меню «Вид» и называется «Строка состояния».

Блокнот

По умолчанию она неактивна, и чтобы её включить, необходимо снять галочку в меню «Формат» -> «Перенос по словам». Далее устанавливаем курсор в конец текста и смотрим в правый нижний угол окна Блокнота. Значение параметра «стлб» и будет длиной текста с пробелами. Обратите внимание, что работает функция только с целыми строками, то есть текст не должен быть разбит на абзацы, в противном случае будет определена длина только последнего абзаца.

Перенос по словам

Если идея с Блокнотом не показалась вам привлекательной, можете попробовать воспользоваться консолью PowerShell. Этот расширенный аналог командной строки имеет функцию length, позволяющую определять длину символов строки. Полностью командлет подсчёта длины строки будет выглядеть следующим образом:

«ваш текст«.length

Как и в случае с Блокнотом, текст не должен содержать переносов, иначе при выполнении команды вы получите ошибку.

PowerShell

В Windows 10 передавать текст в PowerShell можно из буфера обмена командой (Get-Clipboard).length либо указывая путь к текстовому файлу на жёстком диске, например, (Get-Content «D:/Документ.txt»).length. Однако учитывая ограничения на размер вводимого в PowerShell текста, пример с Блокнотом кажется более предпочтительным.

Загрузка…

Расчёт иформационного объема текстового сообщения

Расчёт
информационного объёма текстового
сообщения (количества информации,
содержащейся в информационном сообщении)
основан на подсчёте количества символов
в этом сообщении, включая пробелы, и на
определении информационного веса одного
символа, который зависит от кодировки,
используемой при передаче и хранении
данного сообщения.

В
традиционной кодировке (Windows,
ASCII)
для кодирования одного символа
используется 1 байт (8 бит). Эта величина
и является информационным весом одного
символа. Такой 8-ми разрядный код позволяет
закодировать 256 различных
символов, т.к. 28=256.

В
настоящее время широкое распространение
получил новый международный стандарт
Unicode,
который отводит на каждый символ два
байта (16 бит). С его помощью можно
закодировать 216
=
65536 различных символов.

Итак,
для расчёта информационного объёма
текстового сообщения используется
формула

Vtext
=
nсимв*i
/ kсжатия
, (2)

где
Vtext

это информационный объём текстового
сообщения, измеряющийся в байтах,
килобайтах, мегабайтах; nсимв

количество символов в сообщении, i

информационный вес одного символа,
который измеряется в битах на один
символ; kсжатия
– коэффициент сжатия данных, без сжатия
он равен 1.

Примеры.

Информация
в кодировке Unicode
передается
со скоростью 128 знаков в секунду в течение
32 минут. Какую часть дискеты ёмкостью
1,44Мб займёт переданная информация?

Дано:
v
=
128 символов/сек;
t
=
32 минуты=1920сек;
i
=
16 бит/символ

Решение:

nсимв
=
v*t
=
245760 символов
V=nсимв*i
=
245760*16 = 3932160 бит = 491520 байт = 480 Кб = 0,469Мб,
что составляет 0,469Мб*100%/1,44Мб = 33% объёма
дискеты

Ответ:
33%
объёма дискеты будет занято переданным
сообщением

Расчёт иформационного объема растрового изображения

Расчёт
информационного объёма растрового
графического изображения (количества
информации, содержащейся в графическом
изображении) основан на подсчёте
количества пикселей в этом изображении
и на определении глубины цвета
(информационного веса одного пикселя).

Итак,
для расчёта информационного объёма
растрового графического изображения
используется формула (3):

Vpic
=
K
*
nсимв
*
i
/ kсжатия
, (3)

где
Vpic

это информационный объём растрового
графического изображения, измеряющийся
в байтах, килобайтах, мегабайтах; K

количество пикселей (точек) в изображении,
определяющееся разрешающей способностью
носителя информации (экрана монитора,
сканера, принтера); i

глубина цвета, которая измеряется в
битах на один пиксель; kсжатия
– коэффициент сжатия данных, без сжатия
он равен 1.

Глубина
цвета задаётся количеством битов,
используемым для кодирования цвета
точки. Глубина
цвета связана с количеством отображаемых
цветов формулой
N=2i,
где N

это количество цветов в палитре, i

глубина цвета в битах на один пиксель.

Примеры.

1)
В результате преобразования растрового
графического изображения количество
цветов уменьшилось с 256 до 16. Как при
этом изменится объем видеопамяти,
занимаемой изображением?

Дано:
N1
=
256 цветов;
N2
=
16 цветов;

Решение:

Используем
формулы
V1
=
K*i1;
N1
=
2i1;
V2
=
K*i2;
N2
=
2i2;

N1
=
256 = 28;
i1
=
8 бит/пиксель

N2
=
16 = 24;
i2
=
4 бит/пиксель

V1
=
K*8;
V2
=
K*4;

V2/V1
=
4/8 = 1/2

Ответ:
объём графического изображения уменьшится
в два раза.

2)
Сканируется цветное изображение
стандартного размера А4 (21*29,7 см).
Разрешающая способность сканера 1200dpi
и
глубина цвета 24 бита. Какой информационный
объём будет иметь полученный графический
файл?

Дано:
i
=
24 бита на пиксель;
S
=
21см*29,7 см
D
=
1200 dpi
(точек
на один дюйм)

Решение:

Используем
формулы
V
=
K*i;

1дюйм
= 2,54 см

S
=
(21/2,54)*(29,7/2,54) = 8,3дюймов*11,7дюймов

K
=
1200*8,3*1200*11,7 = 139210118 пикселей

V
=
139210118*24 = 3341042842бита = 417630355байт = 407842Кб =
398Мб

Ответ:
объём сканированного графического
изображения равен 398 Мегабайт

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

    06.03.20161.42 Mб33ОТИ практическая 5.docx

  • #

Данный калькулятор считает количество байт, которое «весит» текст (слово, строка, предложение и тд.) онлайн.

По-умолчанию подсчет ведется в кодировках UTF-8/cp1251/KOI8/CP866, где один символ, занимает один байт. Но можно переключиться в режим подсчета в кодировке UTF-16, где один символ занимает два байта. Сделать это можно в поле «Задачи».

Введите текст (любой набор символов) *

Текст

Укажите символы, которые следует убрать из текста

Регистр букв (для уникальных слов и букв)

Выберите информацию, которую хотите получить

* — обязательно заполнить

Как посчитать длину текста и не привлекать внимание санитаров

Время на прочтение
10 мин

Количество просмотров 30K

Привет! Меня зовут Ивасюта Алексей, я фронтенд-разработчик в Авито в кластере Seller Experience. В этой статье я расскажу, как правильно рассчитать длину текста в Java Script. Эта статья будет одинаково полезна как начинающим разработчикам, так и весьма опытным. Благодаря ей вы поймете устройство Unicode и особенности его работы в JS.

Немного предыстории

На нашей платформе можно размещать объявления и давать им описание в виде текста. Обычно для таких полей есть ограничение на максимальное количество вводимых символов. Если пользователь напечатал больше знаков, чем положено, сработает валидация и объявление нельзя будет разместить. 

В целом, достаточно тривиальная практика. Жили мы, не тужили, разрешали вводить в поля только кириллицу, латиницу и цифры — и все работало прекрасно. Однажды мы подумали: «Почему бы не разрешить пользователям добавлять в описание эмодзи?». Сделали правочки, разрешили ввод новых символов и продолжили жить дальше. Но тут началось интересное: некоторые пользователи стали жаловаться, что они вводят символов меньше лимита, но валидацию описание все равно не проходит. Путем нехитрых проверок удалось найти интересные вещи. Давайте посмотрим на код:

'Фотоаппарат'.length
// 11
'Фотоаппарат📷'.length
// 13

Длина слова «Фотоаппарат» равна 11. Если в конец добавить эмодзи, то длина становится равной 13. Может показаться, что это какой-то баг языка, но нет — всё правильно. И вот тут нам с вами придется нырнуть на самое дно Unicode.

Щепотка теории по Unicode

Unicode — это достаточно старый формат кодирования символов, который максимально широко распространился на данный момент. Он решает сложную задачу перекодирования текста, которая раньше стояла перед всеми разработчиками.

Особенность этого стандарта заключается в том, что за каждым символом, который добавлен в него объединением Unicode Consortium, навсегда закрепляется уникальный идентификатор. При помощи него любая программа на любой машине с любой локализацией может определить, какой символ представлен в тексте. Эти идентификаторы называются кодовыми точками. Значению кодовой точки соответствует всегда один и тот же символ.

В Unicode кодовую точку принято записывать в шестнадцатеричном виде и использовать не менее 4 цифр с ведущими нулями при необходимости. Например, число 0 в шестнадцатеричной системе счисления также имеет значение 0 и будет записано в виде U+0000 (U+ — префикс, обозначающий Unicode-символ). Это пустой символ или же аналог null/nil в Unicode. Обычно этот символ используется для обозначения конца null-терминированных строк в языке С (Си-строки). 

null-терминированные строки

В языке С обработка строки произвольной длины происходит до того момента, пока не встретится нулевой символ.

Четыре разряда в шестнадцатеричной системе позволяют закодировать 164 комбинаций (или 216) — это 65 536 значений, каждое из которых является кодовой точкой. Значения находятся в диапазоне U+0000 — U+FFFF в шестнадцатеричной системе счисления. Этот диапазон значений называется «Базовой многоязычной плоскостью» или BMP. Она включает в себя символы из алфавитов самых распространенных языков мира, математические операторы, геометрические фигуры, специальные символы и многое другое. Важно заметить, что некоторые точки являются зарезервированными, некоторые из них не имеют графического представления, как например U+200D, а некоторые вообще не используются.

После того, как мы изучили теорию, давайте посмотрим на несколько примеров и пощупаем Unicode на практике.

Для получения значения кодовой точки в JS есть метод String.prototype.codePointAt(). Если мы вызовем метод на строке с заглавной кириллической буквой «А», то получим значение кодовой точки в десятичной системе счисления.

'А'.codePointAt(0) // 1040

Теперь переведем полученное значение в шестнадцатеричную систему счисления.

'А'.codePointAt(0).toString(16) // '410'

В JS шестнадцатиразрядные числа можно записывать в формате 0x0000. Таким образом наше число можно записать в виде 0x410.

0x410 // 1040
typeof 0x410 === 'number' // true

Для получения Unicode-символа по значению кодовой точки JS предоставляет статический метод String.fromCodePoint(). Давайте получим нашу букву обратно.

String.fromCodePoint(0x410) // 'А'

Для записи символов Unicode можно использовать формат u0000. Если число меньше четырех знаков, то недостающие заполняются нулями слева. Таким образом нашу букву также можно представить в виде строки.

'u0410' // 'А'

Теперь мы можем даже составить целое слово. Вставьте эту строку в консоль и посмотрите на результат.

'u041fu0440u0438u0432u0435u0442'

Нормализация и комбинируемые символы

Перейдем к неочевидному поведению строк в Java Script. Возьмем для примера такой экзотический символ, как «Слог Хангыль ggag» из слогового письма Хангыля и посчитаем его длину.

'깍'.length // 3

Ого! Символ один, а длина строки почему-то равна трем. На самом деле, этот символ состоит из трех кодовых точек U+1101, U+1161 и U+11A8. Вместе эти знаки в Хангыльском письме образуют иероглиф , который сам является отдельным символом и имеет кодовую точку U+AE4D. 

Здесь мы приходим к двум важным выводам:

  1. Длина строки в JS считается по количеству кодовых точек, из которых она состоит.

  2. При подсчете длины строки надо учитывать количество графем, а не кодовых точек.

Так, у нас появилось новое понятие. Графема — это минимальная единица письменности. В нашем случае, три кодовые точки образуют одну графему, которая отображается в виде иероглифа  .

В Unicode для таких случаев описаны алгоритмы нормализации, когда комбинируемые символы могут быть заменены на один составной. JS предоставляет метод String.prototype.normalize, который позволяет проводить нормализацию строк по описанным в Unicode алгоритмам. После выполнения нормализации три кодовые точки будут заменены на одну — U+AE4D.

'깍'.normalize()
    .codePointAt(0)
    .toString(16) // 'ae4d'

'깍'.normalize('NFC').length // 1

Можно комбинировать различные символы в Unicode, но для результата комбинации может не быть предусмотрено отдельной кодовой точки.

'ко̅д'.normalize().length // 4

Длина строки в этом случае равна 4, а видим мы всего три графемы. Символ о̅ состоит из обычной кириллической буквы «о» и Unicode-символа комбинируемого надчеркивания U+0305. 

'коu0305д' // ко̅д

В таких случаях при учете длины строки нужно исключать из расчета комбинируемые символы. Они не создают отдельных графем, а просто видоизменяют отображение рядом стоящих. Для этого можем воспользоваться экранированием свойств Unicode в регулярках JS.

const regexSymbolWithCombiningMarks = /(P{Mark})(p{Mark}+)/ug;

const countTextLength = (text) => {
    const normalizedText = text
        .normalize('NFC')
        .replace(regexSymbolWithCombiningMarks, function(_, symbol) {
            return symbol;
        });

    return normalizedText.length;
};

countTextLength('ко̅д') // 3
countTextLength('깍') // 1

Селекторы начертания

В Unicode есть диапазон кодовых точек U+FE00 — U+FE0F для селекторов начертания. Это невидимые символы, которые изменяют начертание предшествующих им. Самый интересный — U+FE0F. Этот селектор указывает, что предыдущий символ должен отображаться в виде эмодзи, если предыдущий символ по умолчанию имеет текстовое представление. Например, кодовая точка U+2764 по умолчанию будет отображаться как закрашенное жирное сердечко . Если мы добавим к нему селектор начертания U+FE0F, то отображаться он уже будет в виде эмодзи сердечка ❤️.

'❤ufe0f' // ❤️

Отображение в консоли браузера

В консоли браузера и здесь в примере кода вы все равно увидите отображение в виде закрашенного черного сердечка, но если скопировать полученный символ в мессенджер, то там будет эмодзи.

То же самое можно проделать со снеговиком.

'☃ufe0f' // ☃️

Селекторы начертания не создают отдельных графем, а лишь изменяют отображение предшествующих. Они не должны учитываться при подсчете длины текста. В вычислениях их можно убрать, например, регуляркой.

'❤ufe0E'.replace(/[u{FE00}-u{FE0F}]/ug, '').length // 1

Суррогаты

Однажды IT-сообществу захотелось добавить в Unicode больше символов, а диапазон в 65К кодовых точек стал тесноват. Ребята из Unicode Consortium призадумались и решили заложить в стандарт больше возможностей для дальнейшего расширения. 

Так как надо было сохранить совместимость с уже существующими значениями и сильно увеличить количество допустимых, диапазон кодовых точек расширили до U+10FFFF. В Unicode появилось еще 16 плоскостей по 65 536 символов в каждой. Таким образом, количество возможных значений увеличилось до 1 114 112. Сейчас в Unicode есть эмодзи, кости для маджонга, алхимические символы, символы «Канона великого сокровенного» и куча всего другого. 

Теперь нам надо немного вспомнить азы программирования. Для кодирования каждого символа необходимо n бит. Число 1 в двоичной системе счисления будет иметь вид 1, а для его кодирования нужен 1 бит. Число 65535 будет иметь вид 1111111111111111. У него 16 разрядов, соответственно, для хранения нужно 16 бит, то есть 2 байта.

Существует кодировка UTF-8, которая использует 8 бит на кодирование каждого символа. При помощи нее можно закодировать 256 различных знаков (28). Строки в этой кодировке занимают очень мало места: текст в 10 000 знаков будет занимать 10 000 байт или 9,77 КБ. Это позволяет оптимизировать использование памяти.

Есть кодировка UTF-32, которая использует 32 бита на кодирование каждого символа. Здесь больше 4 миллиардов комбинаций. Как можно догадаться, любой символ из любой плоскости Unicode может быть с легкостью закодирован, но строки в ней занимают много памяти. Так текст в 10 000 знаков уже будет занимать больше 32 КБ.

Самое интересное происходит в системах, которые используют шестнадцатибитную кодировку представления. JavaScript использует именно кодировку UTF-16, которая позволяет выделять на хранение каждого символа 2 байта. Для хранения кодовой точки из BMP этого достаточно, но для точек из других плоскостей нужно больше бит. 

Число 10000 в шестнадцатеричной системе преобразуется в 10000000000000000 в двоичной. У числа 17 разрядов, а значит для кодирование уже нужно 17 бит. Уместить 17 бит в 2 байта никак нельзя. 

Чтобы кодировать символы из астральных плоскостей в 16-битных кодировках были придуманы суррогатные пары. Суррогаты — это зарезервированный диапазон значений в базовой плоскости Unicode, который делится на две части:

  • U+D800 – U+DBFF — верхние суррогаты;

  • U+DC00 – U+DFFF — нижние суррогаты.

В каждый диапазон входит 210 символов. То есть всего возможно 220 комбинаций — это 1 048 576 значений. Добавим сюда 65 536 значений из BMP и получим 1 114 112 значений. Таким образом мы можем закодировать кодовые точки из всех плоскостей Unicode.

Точки из BMP кодируются «как есть». Если же надо закодировать кодовую точку из астральной плоскости, то для нее вычисляется пара суррогатов по формуле:

const highSurrogate = Math.floor((codepoint - 0x10000) / 0x400) + 0xD800;
const lowSurrogate = (codepoint - 0x10000) % 0x400 + 0xDC00;

Для обратного преобразования из суррогатной пары в кодовую точку используется следующая формула:

const codePoint = (highSurrogate - 0xD800) * 0x400 + lowSurrogate - 0xDC00 + 0x10000;

Таким образом, ухмыляющийся смайлик U+1F600 будет преобразован в суррогатную пару U+D83D + U+DE00.

'😀' === 'u{d83d}u{de00}'
// true

Именно поэтому длина этого эмодзи равна 2.

Формат записи кодовых точек

До этого момента вы видели запись кодовых точек в строках только в виде u0000. Такой формат записи является устаревшим и будет работать только для кодовых точек из BMP. Вместо него используйте запись вида u{0000}. Такой формат работает для кодовых точек из любой плоскости.

'😀'.length === 2
// true

Чтобы учитывать эмодзи при подсчёте, в JavaScript есть итератор строк, который учитывает суррогатные пары и итерирует их, как одну графему. Это значит, что длину строки с учетом эмодзи можно подсчитать итерируясь по строке и запоминая количество итераций.

const countGraphemes = (text) => {
    let count = 0;
    for(const _ of text) {
        count++;
    }
    return count;
}
countGraphemes('текст 😀') // 7

То же самое можно сделать разбив строку на массив.

Array.from('текст 😀').length // 7

// или

[...'текст 😀'].length // 7

Модификаторы цвета

В Unicode есть пять модификаторов цвета кожи по шкале Фитцпатрика в диапазоне  U+1F3FB —U+1F3FF.

При помощи этих модификаторов можно персонифицировать «базовые» эмодзи.

'👩u{1f3fb}'
// '👩🏻'

'👧u{1f3fc}'
// '👧🏼'

'🧒u{1f3fd}'
// '🧒🏽'

'👶u{1f3fe}'
// '👶🏾'

'👨u{1f3ff}'
// '👨🏿'

Как вы уже могли догадаться, модификаторы цвета при подсчете длины текста учитывать не надо.

Объединитель нулевой ширины

Давайте подсчитаем длину эмодзи семьи из трех человек.

'👨‍👩‍👦'.length
// 8

Весьма неожиданный результат. Давайте теперь разобьем строку на массив символов.

[...'👨‍👩‍👦']
// ['👨', '‍', '👩', '‍', '👦']

Теперь мы видим настоящую магию: эмодзи семьи из трех человек на самом деле состоит из трех базовых эмодзи, которые соединены объединителем нулевой ширины или ZWJ. Этот символ не имеет графического отображения и представлен кодовой точкой U+200D. А при помощи такой хитрой конструкции мы можем собрать новый эмодзи из базовых.

['👨', 'u{200d}', '👧', 'u{200d}', '👦'].reduce((prev, curr) => prev + curr)
// '👨‍👧‍👦'

Мы получили эмодзи отца-одиночки с двумя детьми.

В этом случае уже сложнее подсчитать правильную длину текста с учетом составных эмодзи, поэтому придется разбивать строку на массив и итерироваться по нему. При этом комбинации, которые состоят из нескольких символов, соединенных ZWJ, необходимо считать за одну графему.

Собираем все вместе

const regex = /[u{FE00}-u{FE0F}]|[u{1F3FB}-u{1F3FF}]/ug;
// u{FE00}-u{FE0F} -- селекторы начертания
// u{1F3FB}-u{1F3FF} - модификаторы цвета

const regexSymbolWithCombiningMarks = /(P{Mark})(p{Mark}+)/ug; // Поиск комбинируемых символов
const joiner = '200d'; // Объединитель нулевой ширины («Zero Width Joiner», ZWJ)
//  Проверяет, является ли символ ZWJ
const isJoiner = (char) => char.charCodeAt(0).toString(16) === joiner;

const countGraphemes = (text) => {
   const normalizedText = text
       // нормализуем строку
       .normalize('NFC')   
       // удаляем селекторы начертания и модификаторы цвета
       .replace(regex, '')
       // удаляем комбинируемые символы
       .replace(regexSymbolWithCombiningMarks, function(_, symbol) {
           return symbol;
       });
   // Разделяем строку на токены. Кодовые точки из суррогатных пар будут корректно 
   // выделены в одну графему.
   const tokens = Array.from(normalizedText);
   let length = 0;
   let isComplexChar = false; // Обработка комплексных символов, склеенных через ZWJ
   
   // Итеррируемся по массиву токенов и комбинации, склеенные через ZWJ 
   // считаем за одиу графему
   for (let i = 0; i < tokens.length; i++) {
       const char = tokens[i];
       const nextChar = tokens[i + 1];

       if (!nextChar) {
           length += 1;
           continue;
       }

       if (isJoiner(nextChar)) {
           isComplexChar = true;
           continue;
       }

       if (!isJoiner(char) && isComplexChar) {
           isComplexChar = false;
       }

       if (!isComplexChar) {
           length += 1;
       }
   }

   return length;
}

countGraphemes('test😄') // 5
countGraphemes('👦🏾👨‍👩‍👧‍👦') // 2
countGraphemes('깍') // 1

//  и так далее

Конечно, это не самая оптимальная реализация и приведенный выше код необходим лишь для примера и наглядности. К тому же, мы все еще не учитываем множество особенностей Unicode. Этот алгоритм будет работать для подавляющего большинства обычных приложений, но по настоящему честный подсчет для всех возможных языков и начертаний он сделать не сможет. 

Intl.Segmenter

Как вы видите, алгоритм подсчета количества графем получается не самым простым. Здесь требуется учитывать множество деталей и быть глубоко погруженным в тему. И вот наконец-то настает очередь Intl.Segmenter специальный класс, который включает сегментацию текста с учетом языковых стандартов. Это позволяет получать значимые элементы из строки, например, графемы, слова или предложения.

const segmenter = new Intl.Segmenter();
const text = '👨‍👨‍👦‍👦깍ко̅д👨🏿‍';

const iterator = segmenter.segment(text)[Symbol.iterator]();
let count = 0;
for (const symbol of iterator) {
  	count++;
}
console.log(count) // 6

Вот так легко и непринуждённо мы смогли в несколько строчек проделать ту же работу. По умолчанию сегментер бьет текст на графемы, но ему можно дополнительно задать локаль и степень сегментации, например, по словам.

const segmenter = new Intl.Segmenter('fr', { granularity: 'word' });

У меня для вас только одна плохая новость: Intl.Segmenter не поддерживается в Firefox, поэтому кроссбраузерное решение сделать на нем не получится. Пока что вам остается искать рабочий полифил для него и подключать в нужном месте при выполнении кода. Также нужно убедиться в работоспособности сегментации через полифил.

Что в результате

Честный подсчет длины введенного пользователем текста — дело непростое из-за особенностей Unicode и 16-битной кодировки. Если вы хотите сделать по-настоящему интернациональное приложение, например, мессенджер, вам придется учитывать все эти сценарии. Эту проблему призван решить Intl.Segmenter, поэтому с нетерпением ждем его поддержки в Firefox.

Полезные ссылки

Таблица символов Unicode 

Подробнее о графемах 

Алгоритмы нормализации Unicode 

Документация Intl.Segmenter на MDN 

Подробнее про Intl.Segmenter

Экранирование свойств Unicode 

Понравилась статья? Поделить с друзьями:
  • Как грамотно составить объяснительную записку на работе образец
  • Как составить оборотно сальдовую ведомость пример заполнения
  • Как составить письмо по рисками
  • Как найти другие поисковые системы
  • Как найти потерявшийся телефон хуавей