Вопрос или проблема
Эта функция возвращает 6, но ожидается, что она вернет 4:
=LEN("🥎A🥎B")
Какую функцию я должен использовать, чтобы получить “реальное количество символов”, ожидая, что один символ в кодировке UTF-8 считается за 1 символ?
Я не смог найти такой встроенной функции, но результат LEN() “правильный” в том смысле, что он спрашивает ваш браузер, какая длина строки, и возвращает 6, потому что ответ браузера на "🥎A🥎B".length
также 6.
Хотя Google Таблицы работают с Unicode, они фактически не работают с UTF-8 – они работают со строками UTF-16, которые предоставляет JavaScript, и поскольку 🥎
(U+1F94E) имеет кодовую точку выше U+FFFF, он не помещается в один 16-битный кодовый элемент, а должен храниться как суррогатная пара (U+D83E U+DD4E). Поэтому строка из четырех символов хранится не как четыре руны UTF-8, а как шесть 16-битных кодовых единиц UTF-16:
U+D83E U+DD4E U+0041 U+D83E U+DD4E U+0042
К сожалению, 16-битные кодовые единицы именно те, которые учитываются методом String.length в JavaScript (что является неприятным особенностью, которую несколько “ранних адоптеров Unicode” языков программирования сохранили для обратной совместимости при переходе мира от строго 16-битного UCS-2 к UTF-16). Если вы выполните "🥎A🥎B".length
в любой консоли JS, он также вернет 6.
(Существует другая функция LENB()
, которая действительно кодирует строку в UTF-8 и возвращает ее длину в байтах, но это не очень помогает.)
Если вы можете использовать Apps Script, то можно написать пользовательскую функцию, которая это делает (хотя не с использованием примера из MDN, так как Apps Script работает на более старой версии JavaScript, которая не поддерживает строки как итерируемые). Например:
function LENU(str) {
var length = 0, i, c;
for (i = 0; i < str.length; i++) {
c = str.charCodeAt(i);
if (c < 0xDC00 || c > 0xDFFF) {
/* Считаем все, кроме низких суррогатов, так как они всегда
* (по крайней мере, в действительном UTF-16) следуют за высоким суррогатом, который
* уже был учтен ранее. */
length++;
}
}
return length;
}
(Примечание: Проверяйте диапазон кодов символов и не вводите случайно 0xDB00, как я только что сделал.)
Это должно быть использовано как =LENU(...)
из таблицы, хотя это немного медленнее из-за вызова удаленной службы Apps.
Как упомянуто в другом ответе, "🥎"
представлен в UTF-16 как суррогатная пара и, следовательно, учитывается как два символа.
Тот ответ показал, как создать пользовательскую функцию LENU
с помощью сценария приложений, которая правильно вычисляет длину с использованием пользовательской функции Google Apps Script.
Если вы не можете или не хотите использовать Google Apps Script, тот же подход можно использовать с помощью встроенных формул Google Таблиц:
=REDUCE(
0,
SPLIT(REGEXREPLACE("🥎A🥎B",," ")," "),
LAMBDA(a,c,a+IF(OR(UNICODE(c)<HEX2DEC("DC00"),UNICODE(c)>HEX2DEC("DFFF")),1,0))
)
Это делит строку на массив, содержащий ее составные символы. Затем оно считает все символы, кроме тех, которые являются низкими суррогатами. Это работает, поскольку (как упомянуто в том другом ответе) все низкие суррогаты предшествуют высокому суррогату, который учитывается.
Это можно превратить в именованную функцию. Например, можно создать функцию с именем “LENU” с аргументом-заполнителем “value” и следующим определением формулы:
=IF(LEN(value)=0,0,REDUCE(
0,
SPLIT(REGEXREPLACE(value,," ")," "),
LAMBDA(a,c,a+IF(OR(UNICODE(c)<HEX2DEC("DC00"),UNICODE(c)>HEX2DEC("DFFF")),1,0))
))
Эту именованную функцию можно использовать так же, как встроенную функцию:
=LENU("🥎A🥎B")
Ответ или решение
Как рассчитать длину строки с учетом UTF-8 символов в Google Sheets?
Если вы столкнулись с проблемой, когда функция LEN()
в Google Sheets возвращает неверное количество символов для строки, содержащей определенные UTF-8 символы, такие как “🥎A🥎B”, это может быть связано с тем, что Google Sheets работает с UTF-16 кодировкой. Это приводит к тому, что некоторые символы (например, смайлы) представлены в виде пары суррогатных символов, что искажает результат.
Почему LEN()
возвращает неверное значение?
При анализе строки 🥎A🥎B
, функция LEN()
возвращает 6, несмотря на то, что ожидается 4. Это объясняется тем, что символ "🥎" (U+1F94E) в кодировке UTF-16 представлен как две 16-битные единицы: U+D83E и U+DD4E. Таким образом, два символа "🥎" в строке фактически занимают 4 единицы UTF-16.
Для более точного подсчета символов в строке, необходимо учитывать только уникальные визуально представленные символы, игнорируя суррогатные пары, следуя этим шагам.
Использование Google Apps Script
Если вы хотите решить эту проблему с помощью Google Apps Script, вы можете создать собственную функцию под названием LENU
, которая будет правильно обрабатывать символы. Ниже представлен код, который делает именно это:
function LENU(str) {
var length = 0, i, c;
for (i = 0; i < str.length; i++) {
c = str.charCodeAt(i);
if (c < 0xDC00 || c > 0xDFFF) {
length++;
}
}
return length;
}
Эта функция перебирает каждый символ строки и увеличивает счетчик, если символ не является низким суррогатным символом.
Альтернативное решение без использования Apps Script
Если вы не хотите использовать Google Apps Script, существует возможность решить эту задачу с помощью встроенных формул Google Sheets. Ниже представлена формула, которая может быть использована для подсчета реальной длины строки:
=REDUCE(
0,
SPLIT(REGEXREPLACE("🥎A🥎B",," ")," "),
LAMBDA(a,c,a+IF(OR(UNICODE(c)<HEX2DEC("DC00"),UNICODE(c)>HEX2DEC("DFFF")),1,0))
)
Эта формула:
- Разбивает строку на отдельные символы.
- Подсчитывает только те символы, которые не относятся к нижним суррогатам.
Такой подход позволяет добиться корректного подсчета длины строки.
Именованная функция для удобства
Вы также можете создать именованную функцию в Google Sheets, чтобы использовать ее более удобно. Например, вы можете определить функцию LENU
следующим образом:
=IF(LEN(value)=0,0,REDUCE(
0,
SPLIT(REGEXREPLACE(value,," ")," "),
LAMBDA(a,c,a+IF(OR(UNICODE(c)<HEX2DEC("DC00"),UNICODE(c)>HEX2DEC("DFFF")),1,0))
))
Теперь вы сможете использовать её как любую встроенную функцию:
=LENU("🥎A🥎B")
Заключение
Общеизвестно, что работа с строками, содержащими сложные символы, может вызывать путаницу, и использование стандартной функции LEN()
в Google Sheets может не обеспечивать нужных результатов. HTML и Apps Script предоставляют гибкие возможности для создания точного и эффективного анализа строк. Выбор подходящего решения зависит от ваших потребностей и навыков.