Существует ли общепринятый метод эффективного преобразования строк JavaScript в ArrayBuffers и наоборот? В частности, я хотел бы иметь возможность записывать содержимое ArrayBuffer localStorage
и читать его обратно.
265
Существует ли общепринятый метод эффективного преобразования строк JavaScript в ArrayBuffers и наоборот? В частности, я хотел бы иметь возможность записывать содержимое ArrayBuffer localStorage
и читать его обратно.
Int8Array
ArrayBufferView
то можно будет просто использовать скобочную запись для копирования символовstring[i] = buffer[i]
и наоборот.Uint16Array
s для 16-битных символов JS), но строки JavaScript являются неизменяемыми, поэтому вы не можете напрямую назначить позицию символа. Я бы все - таки нужен скопироватьString.fromCharCode(x)
каждое значение вUint16Array
нормаль ,Array
а затем вызвать.join()
наArray
.string += String.fromCharCode(buffer[i]);
. Кажется странным, что не было бы встроенных методов для преобразования между строками и типизированными массивами. Они должны были знать, что что-то подобное придет.Ответы:
Обновление 2016 - пять лет спустя в спецификациях появились новые методы (см. Поддержку ниже) для преобразования между строками и типизированными массивами с использованием правильного кодирования.
TextEncoder
Представляет
TextEncoder
собой :Изменить примечание, так как выше было написано: (там же)
*) Обновлены спецификации (W3) и здесь (whatwg).
После создания экземпляра
TextEncoder
он возьмет строку и закодирует ее, используя заданный параметр кодирования:Затем вы, конечно, используете
.buffer
параметр в результате,Uint8Array
чтобы преобразовать подкладкуArrayBuffer
в другое представление, если это необходимо.Просто убедитесь, что символы в строке соответствуют схеме кодирования, например, если вы используете символы вне диапазона UTF-8 в примере, они будут закодированы в два байта вместо одного.
Для общего использования вы должны использовать кодировку UTF-16 для таких вещей, как
localStorage
.TextDecoder
Аналогичным образом, противоположный процесс использует
TextDecoder
:Все доступные типы декодирования можно найти здесь .
Библиотека MDN StringView
Альтернативой этому является использование
StringView
библиотеки (лицензированной как lgpl-3.0), цель которой:давая гораздо больше гибкости. Тем не менее, это потребовало бы от нас ссылку на или встроить эту библиотеку время
TextEncoder
/TextDecoder
строится в в современных браузерах.Служба поддержки
По состоянию на июль / 2018 год:
TextEncoder
(Экспериментальный, на стандартной трассе)источник
var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};
так что вы можете простоvar array = encoder.encode('hello');
TextEncoder
что если у вас есть двоичные данные в строке (например, изображение), вы не хотите использоватьTextEncoder
(очевидно). Символы с кодовыми точками больше 127 производят два байта. Почему у меня есть двоичные данные в строке?cy.fixture(NAME, 'binary')
(cypress
) создает строку.Хотя решения Дениса и Генгкева с использованием Blob / FileReader работают, я бы не стал предлагать такой подход. Это асинхронный подход к простой проблеме, и он намного медленнее, чем прямое решение. Я сделал сообщение в html5rocks с более простым и (гораздо более быстрым) решением: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
И решение:
РЕДАКТИРОВАТЬ:
Кодирование API помогает решить преобразование строки проблемы. Проверьте ответ от Джеффа Посника на Html5Rocks.com на вышеупомянутую оригинальную статью.
Выдержка:
источник
This is a cool text!
20 байт в UTF8 - 40 байт в Unicode. (2)ÄÖÜ
6 байтов в UTF8 - 6 байтов в Unicode. (3)☐☑☒
9 байтов в UTF8 - 6 байтов в Unicode. Если вы хотите сохранить строку как файл UTF8 (через API Blob и File Writer), вы не можете использовать эти 2 метода, потому что ArrayBuffer будет в Unicode, а не в UTF8.String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length
у меня работает в Chrome, но если вместо этого вы используете 246301, я получу ваше исключениеВы можете использовать
TextEncoder
иTextDecoder
из стандарта Encoding , который заполняется библиотекой stringencoding , для преобразования строки в ArrayBuffers и из нее:источник
npm install text-encoding
,var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;
. Нет, спасибо.Blob намного медленнее, чем
String.fromCharCode(null,array);
но это терпит неудачу, если буфер массива становится слишком большим. Лучшее решение, которое я нашел, - это использовать его
String.fromCharCode(null,array);
и разбивать его на операции, которые не будут разбивать стек, но будут работать быстрее, чем один символ за раз.Лучшее решение для буфера большого массива:
Я обнаружил, что это примерно в 20 раз быстрее, чем с использованием BLOB-объектов. Это также работает для больших строк более 100 МБ.
источник
Основываясь на ответе gengkev, я создал функции для обоих способов, потому что BlobBuilder может обрабатывать String и ArrayBuffer:
и
Простой тест:
источник
a[y * w + x] = (x + y) / 2 * 16;
я пыталсяgetBlob("x")
, со многими различными миметипами - не повезло.new BlobBuilder(); bb.append(buf);
наnew Blob([buf])
, приведите ArrayBuffer во второй функции к UintArray черезnew UintArray(buf)
(или все, что подходит для базового типа данных), а затем избавьтесь отgetBlob()
вызовов. Наконец, для чистоты переименуйте bb в blob, потому что он больше не BlobBuilder.Все следующее касается получения двоичных строк из буферов массива.
Я бы порекомендовал не использовать
потому что это
Maximum call stack size exceeded
ошибку в 120000- байтовом буфере (Chrome 29))Если вам нужно синхронное решение, используйте что-то вроде
это так же медленно, как предыдущий, но работает правильно. Похоже, что на момент написания этой статьи не было достаточно быстрого синхронного решения этой проблемы (все библиотеки, упомянутые в этом разделе, используют одинаковый подход для своих синхронных функций).
Но я действительно рекомендую использовать
Blob
+FileReader
подходединственный недостаток (не для всех) в том, что он асинхронный . И это примерно в 8-10 раз быстрее, чем предыдущие решения! (Некоторые детали: синхронное решение в моей среде заняло 950-1050 мс для буфера 2,4 Мб, но решение с FileReader имело время около 100-120 мс для одного и того же объема данных. И я протестировал оба синхронных решения на буфере 100 Кб, и они взяли почти в то же время, поэтому цикл не намного медленнее с использованием 'apply'.)
Кстати: как конвертировать ArrayBuffer в и из String. Автор сравнивает два подхода, подобных мне, и получает совершенно противоположные результаты ( его тестовый код здесь ). Почему такие разные результаты? Вероятно, из-за его тестовой строки длиной 1 КБ (он назвал ее «veryLongStr»). Моим буфером было действительно большое изображение JPEG размером 2,4 Мб.
источник
( Обновление. См. Вторую половину этого ответа, где я (надеюсь) предоставил более полное решение.)
Я также столкнулся с этой проблемой, следующие работы для меня в FF 6 (для одного направления):
К сожалению, конечно, вы получите текстовые представления ASCII значений в массиве, а не символы. Это все еще (должно быть) намного более эффективно чем петля, все же. например. Для приведенного выше примера результатом является
0004000000
, а не несколько нулевых символов & a chr (4).Редактировать:
Посмотрев на MDC здесь , вы можете создать
ArrayBuffer
изArray
следующего:Чтобы ответить на ваш оригинальный вопрос, это позволяет вам преобразовать
ArrayBuffer
<->String
следующим образом:Для удобства здесь приведено
function
преобразование необработанного UnicodeString
вArrayBuffer
(будет работать только с ASCII / однобайтовыми символами)Вышеприведенное позволяет вам перейти от
ArrayBuffer
->String
& назад кArrayBuffer
снова, где строка может быть сохранена, например, в..localStorage
:)Надеюсь это поможет,
Дэн
источник
В отличие от решений здесь, мне нужно было конвертировать в / из данных UTF-8. Для этой цели я кодировал следующие две функции, используя трюк (un) escape / (en) decodeURIComponent. Они довольно бесполезно расходуют память, выделяя в 9 раз длину закодированной строки utf8, хотя они должны быть восстановлены gc. Только не используйте их для 100 МБ текста.
Проверка того, что это работает:
источник
Если у вас есть двоичные данные в строке (полученные из
nodejs
+readFile(..., 'binary')
илиcypress
+cy.fixture(..., 'binary')
, и т. Д.), Вы не можете использоватьTextEncoder
. Поддерживает толькоutf8
. Байты со значениями>= 128
каждый превращается в 2 байта.ES2015:
Uint8Array (33) [2, 134, 140, 186, 82, 70, 108, 182, 233, 40, 143, 247, 29, 76, 245, 206, 29, 87, 48, 160, 78, 225, 242 56, 236, 201, 80, 80, 152, 118, 92, 144, 48
«ºRFl¶é (÷ LõÎW0 Náò8ìÉPPv \ 0»
источник
Я обнаружил, что у меня были проблемы с этим подходом, в основном потому, что я пытался записать вывод в файл, и он не был правильно закодирован. Поскольку JS, похоже, использует кодировку UCS-2 ( источник , источник ), нам нужно расширить это решение еще на шаг, вот мое усовершенствованное решение, которое мне подходит.
У меня не было трудностей с общим текстом, но когда он был арабским или корейским, в выходном файле не было всех символов, вместо этого отображались символы ошибок
Вывод файла:
","10k unit":"",Follow:"Õ©íüY‹","Follow %{screen_name}":"%{screen_name}U“’Õ©íü",Tweet:"ĤüÈ","Tweet %{hashtag}":"%{hashtag} ’ĤüÈY‹","Tweet to %{name}":"%{name}U“xĤüÈY‹"},ko:{"%{followers_count} followers":"%{followers_count}…X \Ì","100K+":"100Ì tÁ","10k unit":"Ì è",Follow:"\°","Follow %{screen_name}":"%{screen_name} Ø \°X0",K:"œ",M:"1Ì",Tweet:"¸","Tweet %{hashtag}":"%{hashtag}
Оригинал:
","10k unit":"万",Follow:"フォローする","Follow %{screen_name}":"%{screen_name}さんをフォロー",Tweet:"ツイート","Tweet %{hashtag}":"%{hashtag} をツイートする","Tweet to %{name}":"%{name}さんへツイートする"},ko:{"%{followers_count} followers":"%{followers_count}명의 팔로워","100K+":"100만 이상","10k unit":"만 단위",Follow:"팔로우","Follow %{screen_name}":"%{screen_name} 님 팔로우하기",K:"천",M:"백만",Tweet:"트윗","Tweet %{hashtag}":"%{hashtag}
Я взял информацию из решения Дениса и этот пост, который я нашел.
Вот мой код:
Это позволяет мне сохранять содержимое в файл без проблем кодирования.
Как это работает: Он в основном берет одиночные 8-байтовые блоки, составляющие символ UTF-8, и сохраняет их как одиночные символы (поэтому символ UTF-8, построенный таким образом, может состоять из 1-4 этих символов). UTF-8 кодирует символы в формате, длина которого варьируется от 1 до 4 байтов. Здесь мы кодируем строку в компоненте URI, а затем берем этот компонент и переводим его в соответствующий 8-байтовый символ. Таким образом, мы не теряем информацию, передаваемую символами UTF8 длиной более 1 байта.
источник
если вы использовали огромный пример массива,
arr.length=1000000
вы можете этот код, чтобы избежать проблем с обратным вызовом стекаобратная функция мангини ответ сверху
источник
Ну, вот несколько запутанный способ сделать то же самое:
Изменить: BlobBuilder уже давно устарел в пользу конструктора Blob, которого не было, когда я впервые писал этот пост. Вот обновленная версия. (И да, это всегда был очень глупый способ сделать преобразование, но это было просто для удовольствия!)
источник
источник
arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
После игры с раствором Манджини для перехода от
ArrayBuffer
кString
-ab2str
(это самый элегантный и полезный один я нашел - спасибо!), У меня были некоторые проблемы при работе с большими массивами. Более конкретно, вызовString.fromCharCode.apply(null, new Uint16Array(buf));
вызывает ошибку:arguments array passed to Function.prototype.apply is too large
,Чтобы решить эту проблему (обойти), я решил обработать ввод
ArrayBuffer
кусками. Таким образом, модифицированное решение:Размер порции установлен на,
2^16
потому что это был размер, который я нашел для работы в моей среде разработки. Установка более высокого значения привела к повторению этой же ошибки. Его можно изменить, установив дляCHUNK_SIZE
переменной другое значение. Важно иметь четное число.Замечание по производительности - я не делал никаких тестов производительности для этого решения. Однако, поскольку он основан на предыдущем решении и может обрабатывать большие массивы, я не вижу причин, почему бы его не использовать.
источник
Смотрите здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (C-подобный интерфейс для строк, основанный на интерфейсе JavaScript ArrayBuffer)
источник
Для node.js, а также для браузеров, использующих https://github.com/feross/buffer
Примечание: решения здесь не работают для меня. Мне нужно поддерживать node.js и браузеры и просто сериализовать UInt8Array в строку. Я мог бы сериализовать его как число [], но это занимает ненужное место. С этим решением мне не нужно беспокоиться о кодировках, так как это base64. На всякий случай другие люди борются с той же проблемой ... Мои два цента
источник
Допустим, у вас есть arrayBuffer binaryStr:
а затем вы назначаете текст государству.
источник
«Собственная» двоичная строка, которую возвращает atob (), представляет собой массив из 1 байта на символ.
Поэтому мы не должны хранить 2 байта в символе.
источник
Да:
источник
Я бы рекомендовал НЕ использовать устаревшие API, такие как BlobBuilder
BlobBuilder давно объявлен устаревшим объектом Blob. Сравните код в ответе Дениса - где используется BlobBuilder - с кодом ниже:
Обратите внимание, насколько он чище и менее раздутый по сравнению с устаревшим методом ... Да, здесь определенно стоит рассмотреть.
источник
См. Https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode
источник
Я использовал это и работает для меня.
источник