Сколько байтов в строке JavaScript?

97

У меня есть строка javascript размером около 500 КБ при отправке с сервера в UTF-8. Как я могу определить его размер в JavaScript?

Я знаю, что JavaScript использует UCS-2, значит это означает 2 байта на символ. Однако зависит ли это от реализации JavaScript? Или по кодировке страницы или, может быть, по типу содержимого?

Пол Биггар
источник
Прибл. ответ будет length * charsize, так что ваша догадка близка.
glasnt
1
Современный JavaScript, например ES6, не только использует UCS-2, подробнее здесь: stackoverflow.com/a/46735247/700206
Whitneyland

Ответы:

36

Stringзначения не зависят от реализации, согласно спецификации ECMA-262 3rd Edition , каждый символ представляет собой один 16-битный блок текста UTF-16 :

4.3.16 Строковое значение

Строковое значение является членом типа String и представляет собой конечную упорядоченную последовательность из нуля или более 16-разрядных целочисленных значений без знака.

ПРИМЕЧАНИЕ. Хотя каждое значение обычно представляет собой одну 16-битную единицу текста UTF-16, язык не налагает никаких ограничений или требований на значения, за исключением того, что они являются 16-битными целыми числами без знака.

Кристиан К. Сальвадо
источник
8
Мое прочтение этого отрывка не подразумевает независимости от реализации.
Пол Биггар
4
UTF-16 не гарантируется, только строки хранятся как 16-битные целые числа.
bjornl
Это зависит только от реализации в отношении UTF-16. Описание 16-битного символа универсально.
Panzercrisis
1
Я думаю, что внутренне Firefox может даже использовать 1 байт на символ для некоторых строк .... blog.mozilla.org/javascript/2014/07/21/…
Михал Чаремза
1
UTF-16 явно не разрешен в том виде, в котором я его читаю. Символы UTF-16 могут иметь до 4 байтов, но в спецификации сказано, что «значения должны быть 16-битными целыми числами без знака». Это означает, что строковые значения JavaScript являются подмножеством UTF-16, однако любая строка UTF-16, содержащая 3- или 4-байтовые символы, не допускается.
Whitneyland
71

Эта функция вернет байтовый размер любой строки UTF-8, которую вы ей передали.

function byteCount(s) {
    return encodeURI(s).split(/%..|./).length - 1;
}

Источник

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

Однако сам язык ECMAScript / JavaScript предоставляет символы в соответствии с UCS-2, а не UTF-16.

Источник

Лаури Охерд
источник
9
.split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)Вместо этого используйте . Ваш фрагмент не работает для строк, которые кодируются как "% uXXXX".
Rob W
Используется для вычисления размера фреймов websocket, дает тот же размер для фрейма String, что и инструменты chrome dev.
user85155
2
Используется для строк javascript, загруженных в s3, s3 отображает точно такой же размер [(byteCount (s)) / 1024) .toFixed (2) + «KiB»]
user85155
42

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

Примеры:

console.info(
  new Blob(['😂']).size,                             // 4
  new Blob(['👍']).size,                             // 4
  new Blob(['😂👍']).size,                           // 8
  new Blob(['👍😂']).size,                           // 8
  new Blob(['I\'m a string']).size,                  // 12

  // from Premasagar correction of Lauri's answer for
  // strings containing lone characters in the surrogate pair range:
  // https://stackoverflow.com/a/39488643/6225838
  new Blob([String.fromCharCode(55555)]).size,       // 3
  new Blob([String.fromCharCode(55555, 57000)]).size // 4 (not 6)
);

П Ройтто
источник
2
Слава богу за капли! Вероятно, это должен быть принятый ответ для современных браузеров.
prasanthv
как импортировать Blob в Node.js?
Alexander Mills
4
Ах, с Node.js мы используем Buffer, например,Buffer.from('😂').length
Александр Миллс
19

Попробуйте эту комбинацию с использованием функции unescape js:

const byteAmount = unescape(encodeURIComponent(yourString)).length

Пример процесса полного кодирования:

const s  = "1 a ф № @ ®"; //length is 11
const s2 = encodeURIComponent(s); //length is 41
const s3 = unescape(s2); //length is 15 [1-1,a-1,ф-2,№-3,@-1,®-2]
const s4 = escape(s3); //length is 39
const s5 = decodeURIComponent(s4); //length is 11
Kinjeiro
источник
4
Функция unescapeJavaScript устарела и не должна использоваться для декодирования универсальных идентификаторов ресурсов (URI). Источник
Лаури Охерд
@LauriOherd Я знаю, что комментарий старый, но: в этом ответе unescapeон не используется для декодирования URI. Он используется для преобразования %xxпоследовательностей в отдельные символы. As encodeURIComponentкодирует строку как UTF-8, представляя кодовые единицы либо как соответствующий ей символ ASCII, либо как %xxпоследовательность, unescape(encodeURIComponent(...))результатом вызова является двоичная строка, содержащая представление UTF-8 исходной строки. .lengthПравильный вызов дает размер в байтах строки, закодированной как UTF-8.
TS
И yes ( un) escapeустарел с 1999 года, но он по-прежнему доступен в каждом браузере ... - Тем не менее, есть веская причина отказаться от него. По сути, нет способа правильно их использовать (кроме кодирования / декодирования UTF8 в сочетании с en- / decodeURI( Component) - или, по крайней мере, я не знаю другого полезного приложения для ( un) escape). И сегодня есть лучшие альтернативы для кодирования / декодирования UTF8 ( TextEncoderи т. Д.)
TS
10

Обратите внимание, что если вы нацеливаетесь на node.js, вы можете использовать Buffer.from(string).length:

var str = "\u2620"; // => "☠"
str.length; // => 1 (character)
Buffer.from(str).length // => 3 (bytes)
maerics
источник
7

UTF-8 кодирует символы, используя от 1 до 4 байтов на кодовую точку. Как указала CMS в принятом ответе, JavaScript будет хранить каждый символ внутри, используя 16 бит (2 байта).

Если вы анализируете каждый символ в строке с помощью цикла и подсчитываете количество байтов, используемых для каждой кодовой точки, а затем умножаете общее количество на 2, у вас должно быть использование памяти JavaScript в байтах для этой закодированной строки UTF-8. Возможно что-то вроде этого:

      getStringMemorySize = function( _string ) {
        "use strict";

        var codePoint
            , accum = 0
        ;

        for( var stringIndex = 0, endOfString = _string.length; stringIndex < endOfString; stringIndex++ ) {
            codePoint = _string.charCodeAt( stringIndex );

            if( codePoint < 0x100 ) {
                accum += 1;
                continue;
            }

            if( codePoint < 0x10000 ) {
                accum += 2;
                continue;
            }

            if( codePoint < 0x1000000 ) {
                accum += 3;
            } else {
                accum += 4;
            }
        }

        return accum * 2;
    }

Примеры:

getStringMemorySize( 'I'    );     //  2
getStringMemorySize( '❤'    );     //  4
getStringMemorySize( '𠀰'   );     //  8
getStringMemorySize( 'I❤𠀰' );     // 14
Mac
источник
7

Я использую 3 способа:

  1. TextEncoder ()

    (new TextEncoder().encode("myString")).length)

  2. Blob

    new Blob(["myString"]).size)

  3. Буфер

    Buffer.byteLength("myString", 'utf8'))

Хонг Ли
источник
5

Размер строки JavaScript

  • Pre-ES6 : 2 байта на символ
  • ES6 и выше: 2 байта на символ или 5 или более байтов на символ

Pre-ES6
Всегда 2 байта на символ. UTF-16 не допускается, потому что в спецификации сказано, что «значения должны быть 16-битными целыми числами без знака». Поскольку строки UTF-16 могут использовать 3- или 4-байтовые символы, это нарушит требование 2 байта. Важно отметить, что хотя UTF-16 не может полностью поддерживаться, стандарт требует, чтобы используемые двухбайтовые символы были действительными символами UTF-16. Другими словами, строки JavaScript до ES6 поддерживают подмножество символов UTF-16.

ES6 и более поздние версии
2 байта на символ или 5 или более байтов на символ. Дополнительные размеры вступают в игру, потому что ES6 (ECMAScript 6) добавляет поддержку экранирования кодовой точки Unicode . Использование escape-последовательности Unicode выглядит так: \ u {1D306}

Практические заметки

  • Это не относится к внутренней реализации конкретного движка. Например, некоторые движки используют структуры данных и библиотеки с полной поддержкой UTF-16, но то, что они предоставляют извне, не обязательно должно быть полной поддержкой UTF-16. Также движок может обеспечивать внешнюю поддержку UTF-16, но не обязан это делать.

  • Для ES6 практически говорящие символы никогда не будут длиннее 5 байтов (2 байта для точки перехода + 3 байта для кодовой точки Unicode), потому что последняя версия Unicode имеет только 136 755 возможных символов, что легко умещается в 3 байта. Однако это технически не ограничивается стандартом, поэтому в принципе один символ может использовать, скажем, 4 байта для кодовой точки и всего 6 байтов.

  • Большинство приведенных здесь примеров кода для расчета размера байта, похоже, не учитывают escape-последовательности кодовых точек ES6 Unicode, поэтому в некоторых случаях результаты могут быть неверными.

Whitneyland
источник
1
Просто интересно, если размер 2 байта на символ, почему Buffer.from('test').lengthи Buffer.byteLength('test')равен 4 (в Node) , а new Blob(['test']).sizeтакже равно 4?
user1063287
Pre-ES6: разрешен UTF-16: см. 3-е издание ECMA-262 (с 1999 г.) : на первой странице говорится, что разрешены UCS2 или UTF-16. Страница 5, определение строкового значения: «... Хотя каждое значение обычно представляет собой единственную 16-битную единицу текста UTF-16, ...». На странице 81 приведена таблица, в которой показано, как совпадающие суррогатные пары должны быть закодированы как четыре байта UTF-8.
TS
«за символ» - если под этим вы имеете в виду «за воспринимаемый пользователем символ» ( спецификация , более простое объяснение ), это может быть любое количество 16-битных кодовых единиц. Если вы имели в виду «кодовую точку», это может быть одна или две 16-битные кодовые единицы в UTF-16 . (Это не может быть 2,5 единицы кода (или как получить 5 байтов?))
TS
В стандарте не определено, действительно ли каждый элемент в строке javascript ( 16-битные целые числа без знака («элементы») ) внутренне представлен двумя байтами. (И как это могло быть - пока интерфейс, предоставляемый программе javascript, соответствует стандарту, все работает так, как задумано.) Mozilla, например, может использовать только один байт на
TS
Экраны кодовой точки Unicode не имеют ничего общего с длиной строки - это просто новый способ представления строк в исходном коде. ( '\u{1F600}'.length===2, '\u{1F600}'==='\uD83D\uDE00', '\u{1F600}'==='😀')
TS
3

Один элемент в строке JavaScript считается одной единицей кода UTF-16. Другими словами, символы String хранятся в 16-битном формате (1 единица кода), а 16-битный код равен 2 байтам (8-битный = 1 байт).

В charCodeAt()Метод может быть использован , чтобы возвращать целое число в диапазоне от 0 до 65535 , представляющего кода блока UTF-16 по данному индексу.

В codePointAt()Может быть использовано , чтобы вернуть все значения точки коды для символов Unicode, например UTF-32.

Когда символ UTF-16 не может быть представлен в одной 16-битной кодовой единице, он будет иметь суррогатную пару и, следовательно, использовать две кодовые единицы (2 x 16-битные = 4 байта)

См. Кодировки Unicode для получения информации о различных кодировках и их диапазонах кодов.

Holmberd
источник
То, что вы говорите о суррогатах, похоже, нарушает спецификацию сценария ECMA. Как я прокомментировал выше, в спецификации требуется два байта на символ, и разрешение суррогатных пар нарушит это.
Whitneyland
Механизмы Javascript ES5 внутренне свободны для использования USC-2 или UTF-16, но на самом деле они используют своего рода UCS-2 с суррогатами. Это потому, что он позволяет отображать суррогатные половины как отдельные символы, одиночные целые числа без знака UTF-16. Если вы используете в исходном коде символ Юникода, который требует представления более одной 16-битной кодовой единицы, будет использоваться суррогатная пара. Такое поведение не противоречит
holmberd
2

Ответ Лаури Охерда хорошо работает для большинства строк, которые можно увидеть в дикой природе, но не удастся, если строка содержит одиночные символы в диапазоне суррогатных пар, от 0xD800 до 0xDFFF. Например

byteCount(String.fromCharCode(55555))
// URIError: URI malformed

Эта более длинная функция должна обрабатывать все строки:

function bytes (str) {
  var bytes=0, len=str.length, codePoint, next, i;

  for (i=0; i < len; i++) {
    codePoint = str.charCodeAt(i);

    // Lone surrogates cannot be passed to encodeURI
    if (codePoint >= 0xD800 && codePoint < 0xE000) {
      if (codePoint < 0xDC00 && i + 1 < len) {
        next = str.charCodeAt(i + 1);

        if (next >= 0xDC00 && next < 0xE000) {
          bytes += 4;
          i++;
          continue;
        }
      }
    }

    bytes += (codePoint < 0x80 ? 1 : (codePoint < 0x800 ? 2 : 3));
  }

  return bytes;
}

Например

bytes(String.fromCharCode(55555))
// 3

Он правильно рассчитает размер строк, содержащих суррогатные пары:

bytes(String.fromCharCode(55555, 57000))
// 4 (not 6)

Результаты можно сравнить со встроенной функцией Node Buffer.byteLength:

Buffer.byteLength(String.fromCharCode(55555), 'utf8')
// 3

Buffer.byteLength(String.fromCharCode(55555, 57000), 'utf8')
// 4 (not 6)
Премасагар
источник
1

Я работаю со встроенной версией двигателя V8. Я тестировал одну строку. Толкаем каждый шаг 1000 знаков. UTF-8.

Первый тест с однобайтовым (8 бит, ANSI) символом «А» (шестнадцатеричный: 41). Второй тест с двухбайтовым символом (16 бит) «Ω» (шестнадцатеричный: CE A9) и третий тест с трехбайтовым символом (24 бит) «☺» (шестнадцатеричный: E2 98 BA).

Во всех трех случаях устройство распечатывает из памяти 888 000 знаков и использует ок. 26 348 кб в ОЗУ.

Результат: символы не сохраняются динамически. И не только с 16 бит. - Хорошо, возможно, только для моего случая (встроенное устройство ОЗУ 128 МБ, V8 Engine C ++ / QT). - Кодировка символов не имеет ничего общего с размером в оперативной памяти механизма javascript. Например, encodingURI и т. Д. Полезен только для передачи и хранения данных высокого уровня.

Встроенные или нет, но символы хранятся не только в 16-битном формате. К сожалению, у меня нет 100% ответа, что Javascript делает на низком уровне. Кстати. Я тестировал то же самое (первый тест выше) с массивом символа "A". Выталкивал 1000 предметов на каждом шагу. (Точно такой же тест. Просто заменила строку на массив) И система выявила нехватку памяти (требуется) после использования 10 416 КБ и длины массива 1 337 000. Итак, движок javascript не ограничен просто. Это немного сложнее.

Доминик
источник
0

Вы можете попробовать это:

  var b = str.match(/[^\x00-\xff]/g);
  return (str.length + (!b ? 0: b.length)); 

У меня это сработало.

user3728331
источник
1
Конечно, это предполагает, что все символы имеют максимум 2 байта? Если есть 3- или 4-байтовые символы (которые возможны в UTF-8), тогда эта функция будет считать их только как 2-байтовые символы?
Адам Берли