Преобразование между строками и ArrayBuffers
Существует ли общепринятая методика эффективного преобразования строк JavaScript в ArrayBuffers и наоборот? В частности, я хотел бы иметь возможность записать содержимое ArrayBuffer в localStorage
и прочитать его обратно.
30 ответов
Обновление 2016 - пять лет спустя в спецификациях появились новые методы (см. Поддержку ниже) для преобразования между строками и типизированными массивами с использованием правильного кодирования.
TextEncoder
TextEncoder
представляет:
TextEncoder
Интерфейс представляет кодировщик для конкретного метода, который является конкретной кодировкой символов, напримерutf-8
,Кодировщик принимает поток кодовых точек в качестве входных данных и испускает поток байтов.iso-8859-2
,koi8
,cp1261
,gbk
...
Изменить примечание, поскольку выше было написано: (там же)
Примечание: Firefox, Chrome и Opera раньше поддерживали типы кодирования, отличные от utf-8 (такие как utf-16, iso-8859-2, koi8, cp1261 и gbk). Начиная с Firefox 48 [...], Chrome 54 [...] и Opera 41, нет других типов кодирования, кроме utf-8, для соответствия спецификации. *
*) Обновлены спецификации (W3) и здесь (whatwg).
После создания экземпляра TextEncoder
он возьмет строку и закодирует ее, используя заданный параметр кодирования:
if (!("TextEncoder" in window))
alert("Sorry, this browser does not support TextEncoder...");
var enc = new TextEncoder(); // always utf-8
console.log(enc.encode("This is a string converted to a Uint8Array"));
Вы тогда, конечно, используете .buffer
параметр на результирующий Uint8Array
преобразовать подложку ArrayBuffer
на другой взгляд, если это необходимо.
Просто убедитесь, что символы в строке соответствуют схеме кодирования, например, если вы используете символы вне диапазона UTF-8 в примере, они будут закодированы в два байта вместо одного.
Для общего использования вы бы использовали кодировку UTF-16 для таких вещей, как localStorage
,
TextDecoder
Аналогичным образом, противоположный процесс использует TextDecoder
:
TextDecoder
Интерфейс представляет собой декодер для конкретного метода, то есть конкретной кодировки символов, например,utf-8
,iso-8859-2
,koi8
,cp1261
,gbk
,... Декодер принимает поток байтов в качестве входных данных и выдает поток кодовых точек.
Все доступные типы декодирования можно найти здесь.
if (!("TextDecoder" in window))
alert("Sorry, this browser does not support TextDecoder...");
var enc = new TextDecoder("utf-8");
var arr = new Uint8Array([84,104,105,115,32,105,115,32,97,32,85,105,110,116,
56,65,114,114,97,121,32,99,111,110,118,101,114,116,
101,100,32,116,111,32,97,32,115,116,114,105,110,103]);
console.log(enc.decode(arr));
Библиотека MDN StringView
Альтернативой этому является использование StringView
библиотека (лицензируется как lgpl-3.0), целью которой является:
- создать C-подобный интерфейс для строк (т. е. массив кодов символов - ArrayBufferView в JavaScript) на основе интерфейса JavaScript ArrayBuffer
- создать расширяемую библиотеку, которую каждый может расширить, добавив методы к объекту StringView.prototype
- создать коллекцию методов для таких строковых объектов (так как теперь: stringViews), которые работают строго на массивах чисел, а не на создании новых неизменяемых строк JavaScript
- для работы с кодировками Unicode, отличными от стандартных JavaScript-строк UTF-16
давая гораздо больше гибкости. Тем не менее, это потребует от нас ссылки на эту библиотеку или встраивания ее в TextEncoder
/ TextDecoder
встраивается в современные браузеры.
Служба поддержки
По состоянию на июль /2018 год:
TextEncoder
(Экспериментальный, на стандартной трассе)
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | ? | - | 38
°) 18: Firefox 18 implemented an earlier and slightly different version
of the specification.
WEB WORKER SUPPORT:
Experimental, On Standard Track
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | ? | - | 38
Data from MDN - `npm i -g mdncomp` by epistemex
Хотя решения Дениса и Генгкева с использованием Blob/FileReader работают, я бы не стал предлагать такой подход. Это асинхронный подход к простой проблеме, и он намного медленнее, чем прямое решение. Я сделал сообщение в html5rocks с более простым и (гораздо более быстрым) решением: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
И решение:
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
РЕДАКТИРОВАТЬ:
API кодирования помогает решить проблему преобразования строк. Проверьте ответ от Джеффа Посника на Html5Rocks.com на вышеупомянутую оригинальную статью.
Выдержка:
API-интерфейс кодирования упрощает перевод между необработанными байтами и собственными строками JavaScript независимо от того, с каким из множества стандартных кодировок вам нужно работать.
<pre id="results"></pre>
<script>
if ('TextDecoder' in window) {
// The local files to be fetched, mapped to the encoding that they're using.
var filesToEncoding = {
'utf8.bin': 'utf-8',
'utf16le.bin': 'utf-16le',
'macintosh.bin': 'macintosh'
};
Object.keys(filesToEncoding).forEach(function(file) {
fetchAndDecode(file, filesToEncoding[file]);
});
} else {
document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
}
// Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
function fetchAndDecode(file, encoding) {
var xhr = new XMLHttpRequest();
xhr.open('GET', file);
// Using 'arraybuffer' as the responseType ensures that the raw data is returned,
// rather than letting XMLHttpRequest decode the data first.
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (this.status == 200) {
// The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
var dataView = new DataView(this.response);
// The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
var decoder = new TextDecoder(encoding);
var decodedString = decoder.decode(dataView);
// Add the decoded file's text to the <pre> element on the page.
document.querySelector('#results').textContent += decodedString + '\n';
} else {
console.error('Error while requesting', file, this);
}
};
xhr.send();
}
</script>
Ты можешь использовать TextEncoder
а также TextDecoder
из стандарта Encoding, который заполняется библиотекой stringencoding для преобразования строки в ArrayBuffers и из нее:
var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
Blob намного медленнее, чем String.fromCharCode(null,array);
но это терпит неудачу, если буфер массива становится слишком большим. Лучшее решение, которое я нашел, это использовать String.fromCharCode(null,array);
и разделить его на операции, которые не будут уничтожать стек, но будут быстрее, чем один символ за раз.
Лучшее решение для буфера большого массива:
function arrayBufferToString(buffer){
var bufView = new Uint16Array(buffer);
var length = bufView.length;
var result = '';
var addition = Math.pow(2,16)-1;
for(var i = 0;i<length;i+=addition){
if(i + addition > length){
addition = length - i;
}
result += String.fromCharCode.apply(null, bufView.subarray(i,i+addition));
}
return result;
}
Я обнаружил, что это примерно в 20 раз быстрее, чем с использованием BLOB-объектов. Это также работает для больших строк более 100 МБ.
ES2015:
a=Uint8Array.from(s,(x)=>x.charCodeAt(0))
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
s=String.fromCharCode.apply(null,a)
"ºRFl¶é (÷ LõÎW0 Náò8ìÉPPv \ 0"
Основываясь на ответе gengkev, я создал функции для обоих способов, потому что BlobBuilder может обрабатывать String и ArrayBuffer:
function string2ArrayBuffer(string, callback) {
var bb = new BlobBuilder();
bb.append(string);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
}
f.readAsArrayBuffer(bb.getBlob());
}
а также
function arrayBuffer2String(buf, callback) {
var bb = new BlobBuilder();
bb.append(buf);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result)
}
f.readAsText(bb.getBlob());
}
Простой тест:
string2ArrayBuffer("abc",
function (buf) {
var uInt8 = new Uint8Array(buf);
console.log(uInt8); // Returns `Uint8Array { 0=97, 1=98, 2=99}`
arrayBuffer2String(buf,
function (string) {
console.log(string); // returns "abc"
}
)
}
)
Просто
const buffer = thisReturnsBuffers();
const blob = new Blob([buffer], {type: 'text/plain; charset=utf-8'});
blob.text().then(text => console.log(text));
Или же
const stringVal = "string here";
const blob = new Blob([stringVal], {type: 'text/plain; charset=utf-8'});
blob.arrayBuffer().then(buffer => console.log(buffer));
Почему вы все так усложняете?
Все следующее касается получения двоичных строк из буферов массива.
Я бы порекомендовал не использовать
var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));
потому что это
- вылетает на больших буферах (кто-то писал о "волшебном" размере 246300, но я получил
Maximum call stack size exceeded
ошибка в буфере 120000 байт (Chrome 29) - у него очень низкая производительность (см. ниже)
Если вам нужно синхронное решение, используйте что-то вроде
var
binaryString = '',
bytes = new Uint8Array(arrayBuffer),
length = bytes.length;
for (var i = 0; i < length; i++) {
binaryString += String.fromCharCode(bytes[i]);
}
это так же медленно, как предыдущий, но работает правильно. Кажется, что на момент написания этой статьи не было достаточно быстрого синхронного решения этой проблемы (все библиотеки, упомянутые в этом разделе, используют один и тот же подход для своих синхронных функций).
Но я действительно рекомендую использовать Blob
+ FileReader
подход
function readBinaryStringFromArrayBuffer (arrayBuffer, onSuccess, onFail) {
var reader = new FileReader();
reader.onload = function (event) {
onSuccess(event.target.result);
};
reader.onerror = function (event) {
onFail(event.target.error);
};
reader.readAsBinaryString(new Blob([ arrayBuffer ],
{ type: 'application/octet-stream' }));
}
единственный недостаток (не для всех) в том, что он асинхронный. И это примерно в 8-10 раз быстрее, чем предыдущие решения! (Некоторые детали: синхронное решение в моей среде заняло 950-1050 мс для буфера 2,4 Мб, но решение с FileReader имело время около 100-120 мс для одного и того же объема данных. И я проверил оба синхронных решения в буфере 100 Кб, и они взяли почти в то же время, поэтому цикл не намного медленнее с использованием 'apply'.)
Кстати: как конвертировать ArrayBuffer в и из String. Автор сравнивает два подхода, таких как я, и получает совершенно противоположные результаты ( его тестовый код здесь). Почему такие разные результаты? Вероятно, из-за его тестовой строки длиной 1 КБ (он назвал ее "veryLongStr"). Моим буфером было действительно большое изображение JPEG размером 2,4 Мб.
(Обновление. См. Вторую половину этого ответа, где я (надеюсь) предоставил более полное решение.)
Я также столкнулся с этой проблемой, следующие работы для меня в FF 6 (для одного направления):
var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));
К сожалению, конечно, вы получите текстовые представления ASCII значений в массиве, а не символы. Это все еще (должно быть) намного более эффективно чем петля, все же. например. Для примера выше, результат 0004000000
вместо нескольких нулевых символов & chr(4).
Редактировать:
Посмотрев MDC здесь, вы можете создать ArrayBuffer
из Array
следующее:
var arr = new Array(23);
// New Uint8Array() converts the Array elements
// to Uint8s & creates a new ArrayBuffer
// to store them in & a corresponding view.
// To get at the generated ArrayBuffer,
// you can then access it as below, with the .buffer property
var buf = new Uint8Array( arr ).buffer;
Чтобы ответить на ваш оригинальный вопрос, это позволяет конвертировать ArrayBuffer
<-> String
следующее:
var buf, view, str;
buf = new ArrayBuffer( 256 );
view = new Uint8Array( buf );
view[ 0 ] = 7; // Some dummy values
view[ 2 ] = 4;
// ...
// 1. Buffer -> String (as byte array "list")
str = bufferToString(buf);
alert(str); // Alerts "7,0,4,..."
// 1. String (as byte array) -> Buffer
buf = stringToBuffer(str);
alert(new Uint8Array( buf )[ 2 ]); // Alerts "4"
// Converts any ArrayBuffer to a string
// (a comma-separated list of ASCII ordinals,
// NOT a string of characters from the ordinals
// in the buffer elements)
function bufferToString( buf ) {
var view = new Uint8Array( buf );
return Array.prototype.join.call(view, ",");
}
// Converts a comma-separated ASCII ordinal string list
// back to an ArrayBuffer (see note for bufferToString())
function stringToBuffer( str ) {
var arr = str.split(",")
, view = new Uint8Array( arr );
return view.buffer;
}
Для удобства вот function
для преобразования необработанного Unicode String
для ArrayBuffer
(будет работать только с ASCII/ однобайтовыми символами)
function rawStringToBuffer( str ) {
var idx, len = str.length, arr = new Array( len );
for ( idx = 0 ; idx < len ; ++idx ) {
arr[ idx ] = str.charCodeAt(idx) & 0xFF;
}
// You may create an ArrayBuffer from a standard array (of values) as follows:
return new Uint8Array( arr ).buffer;
}
// Alerts "97"
alert(new Uint8Array( rawStringToBuffer("abc") )[ 0 ]);
Вышеуказанное позволяет вам перейти от ArrayBuffer
-> String
& вернуться к ArrayBuffer
опять же, где строка может быть сохранена, например, в. .localStorage
:)
Надеюсь это поможет,
Дэн
В отличие от решений здесь, мне нужно было конвертировать в / из данных UTF-8. Для этого я кодировал следующие две функции, используя трюк (un)escape/(en)decodeURIComponent. Они довольно бесполезно расходуют память, выделяя в 9 раз длину закодированной строки utf8, хотя они должны быть восстановлены gc. Только не используйте их для 100 МБ текста.
function utf8AbFromStr(str) {
var strUtf8 = unescape(encodeURIComponent(str));
var ab = new Uint8Array(strUtf8.length);
for (var i = 0; i < strUtf8.length; i++) {
ab[i] = strUtf8.charCodeAt(i);
}
return ab;
}
function strFromUtf8Ab(ab) {
return decodeURIComponent(escape(String.fromCharCode.apply(null, ab)));
}
Проверка того, что это работает:
strFromUtf8Ab(utf8AbFromStr('latinкирилицаαβγδεζηあいうえお'))
-> "latinкирилицаαβγδεζηあいうえお"
Для node.js, а также для браузеров, использующих https://github.com/feross/buffer
function ab2str(buf: Uint8Array) {
return Buffer.from(buf).toString('base64');
}
function str2ab(str: string) {
return new Uint8Array(Buffer.from(str, 'base64'))
}
Примечание. Решения здесь не помогли мне. Мне нужно поддерживать node.js и браузеры и просто сериализовать UInt8Array в строку. Я мог бы сериализовать его как число [], но это занимает ненужное место. С этим решением мне не нужно беспокоиться о кодировках, так как это base64. На всякий случай, если другие люди борются с той же проблемой... Мои два цента
Я обнаружил, что у меня были проблемы с этим подходом, в основном потому, что я пытался записать вывод в файл, и он не был правильно закодирован. Поскольку 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}
Я взял информацию из решения Дениса и этот пост, который я нашел.
Вот мой код:
function encode_utf8(s) {
return unescape(encodeURIComponent(s));
}
function decode_utf8(s) {
return decodeURIComponent(escape(s));
}
function ab2str(buf) {
var s = String.fromCharCode.apply(null, new Uint8Array(buf));
return decode_utf8(decode_utf8(s))
}
function str2ab(str) {
var s = encode_utf8(str)
var buf = new ArrayBuffer(s.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=s.length; i<strLen; i++) {
bufView[i] = s.charCodeAt(i);
}
return bufView;
}
Это позволяет мне сохранять содержимое в файл без проблем с кодировкой.
Как это работает: Он в основном берет одиночные 8-байтовые блоки, составляющие символ UTF-8, и сохраняет их как отдельные символы (следовательно, символ UTF-8, построенный таким образом, может состоять из 1-4 этих символов). UTF-8 кодирует символы в формате, длина которого варьируется от 1 до 4 байтов. Здесь мы кодируем строку в компоненте URI, а затем берем этот компонент и переводим его в соответствующий 8-байтовый символ. Таким образом, мы не теряем информацию, передаваемую символами UTF8 длиной более 1 байта.
Если вы использовали огромный пример массива arr.length=1000000
Вы можете этот код, чтобы избежать проблем с обратным вызовом стека
function ab2str(buf) {
var bufView = new Uint16Array(buf);
var unis =""
for (var i = 0; i < bufView.length; i++) {
unis=unis+String.fromCharCode(bufView[i]);
}
return unis
}
обратная функция мангини ответ сверху
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
Ниже представлена рабочая реализация Typescript:
bufferToString(buffer: ArrayBuffer): string {
return String.fromCharCode.apply(null, Array.from(new Uint16Array(buffer)));
}
stringToBuffer(value: string): ArrayBuffer {
let buffer = new ArrayBuffer(value.length * 2); // 2 bytes per char
let view = new Uint16Array(buffer);
for (let i = 0, length = value.length; i < length; i++) {
view[i] = value.charCodeAt(i);
}
return buffer;
}
Я использовал это для множества операций при работе с crypto.subtle.
Ну, вот несколько запутанный способ сделать то же самое:
var string = "Blah blah blah", output;
var bb = new (window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder)();
bb.append(string);
var f = new FileReader();
f.onload = function(e) {
// do whatever
output = e.target.result;
}
f.readAsArrayBuffer(bb.getBlob());
Редактировать: BlobBuilder уже давно устарел в пользу конструктора Blob, которого не было, когда я впервые писал этот пост. Вот обновленная версия. (И да, это всегда был очень глупый способ сделать преобразование, но это было просто для удовольствия!)
var string = "Blah blah blah", output;
var f = new FileReader();
f.onload = function(e) {
// do whatever
output = e.target.result;
};
f.readAsArrayBuffer(new Blob([string]));
Недавно мне также нужно было сделать это для одного из моих проектов, поэтому я провел хорошее исследование и получил результат от сообщества разработчиков Google, в котором это просто говорится:
Для ArrayBuffer в String
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
// Here Uint16 can be different like Uinit8/Uint32 depending upon your buffer value type.
Для String в ArrayBuffer
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
//Same here also for the Uint16Array.
Для получения более подробной информации вы можете обратиться к этому блогу от Google.
stringToArrayBuffer(byteString) {
var byteArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
byteArray[i] = byteString.codePointAt(i);
}
return byteArray;
}
arrayBufferToString(buffer) {
var byteArray = new Uint8Array(buffer);
var byteString = '';
for (var i = 0; i < byteArray.byteLength; i++) {
byteString += String.fromCodePoint(byteArray[i]);
}
return byteString;
}
Допустим, у вас есть arrayBuffer binaryStr:
let text = String.fromCharCode.apply(null, new Uint8Array(binaryStr));
а затем вы назначаете текст состоянию.
Для меня это сработало хорошо.
static async hash(message) {
const data = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
return hashHex
}
Да:
const encstr = (`TextEncoder` in window) ? new TextEncoder().encode(str) : Uint8Array.from(str, c => c.codePointAt(0));
Смотрите здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView(C-подобный интерфейс для строк, основанный на интерфейсе JavaScript ArrayBuffer)
Я использовал это и работает для меня.
function arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
-> ->
String(Base64)
Изменять
ArrayBuffer
к
Buffer
а затем в
String
.
Buffer.from(arrBuffer).toString("base64");
После игры с Манджини решение для преобразования из ArrayBuffer
в String
- ab2str
(который является самым элегантным и полезным, который я нашел - спасибо!), у меня были некоторые проблемы при работе с большими массивами. Более конкретно, зовет String.fromCharCode.apply(null, new Uint16Array(buf));
выдает ошибку:
arguments array passed to Function.prototype.apply is too large
,
Чтобы решить эту проблему (обойти), я решил обработать ввод ArrayBuffer
кусками Таким образом, модифицированное решение:
function ab2str(buf) {
var str = "";
var ab = new Uint16Array(buf);
var abLen = ab.length;
var CHUNK_SIZE = Math.pow(2, 16);
var offset, len, subab;
for (offset = 0; offset < abLen; offset += CHUNK_SIZE) {
len = Math.min(CHUNK_SIZE, abLen-offset);
subab = ab.subarray(offset, offset+len);
str += String.fromCharCode.apply(null, subab);
}
return str;
}
Размер порции установлен на 2^16
потому что это был размер, который я нашел, чтобы работать в моей среде разработки. Установка более высокого значения привела к повторению этой же ошибки. Это можно изменить, установив CHUNK_SIZE
переменная к другому значению. Важно иметь четное число.
Обратите внимание на производительность - я не делал никаких тестов производительности для этого решения. Однако, поскольку он основан на предыдущем решении и может обрабатывать большие массивы, я не вижу причин, почему бы его не использовать.
Любые комментарии приветствуются (-:
Используйте распаковку splat вместо петель:
Для подстрок
arrbuf.slice()
может быть трудоустроен.
"Нативная" двоичная строка, которую возвращает atob(), представляет собой массив из 1 байта на символ.
Поэтому мы не должны хранить 2 байта в символе.
var arrayBufferToString = function(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
var stringToArrayBuffer = function(str) {
return (new Uint8Array([].map.call(str,function(x){return x.charCodeAt(0)}))).buffer;
}
Я бы рекомендовал НЕ использовать устаревшие API, такие как BlobBuilder
BlobBuilder давно объявлен устаревшим объектом Blob. Сравните код в ответе Дениса - где используется BlobBuilder - с кодом ниже:
function arrayBufferGen(str, cb) {
var b = new Blob([str]);
var f = new FileReader();
f.onload = function(e) {
cb(e.target.result);
}
f.readAsArrayBuffer(b);
}
Заметьте, насколько он чище и менее раздутый по сравнению с устаревшим методом... Да, здесь определенно стоит рассмотреть.
Из emscripten:
function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) {
if (!(maxBytesToWrite > 0)) return 0;
var startIdx = outIdx;
var endIdx = outIdx + maxBytesToWrite - 1;
for (var i = 0; i < str.length; ++i) {
var u = str.charCodeAt(i);
if (u >= 55296 && u <= 57343) {
var u1 = str.charCodeAt(++i);
u = 65536 + ((u & 1023) << 10) | u1 & 1023
}
if (u <= 127) {
if (outIdx >= endIdx) break;
outU8Array[outIdx++] = u
} else if (u <= 2047) {
if (outIdx + 1 >= endIdx) break;
outU8Array[outIdx++] = 192 | u >> 6;
outU8Array[outIdx++] = 128 | u & 63
} else if (u <= 65535) {
if (outIdx + 2 >= endIdx) break;
outU8Array[outIdx++] = 224 | u >> 12;
outU8Array[outIdx++] = 128 | u >> 6 & 63;
outU8Array[outIdx++] = 128 | u & 63
} else {
if (outIdx + 3 >= endIdx) break;
outU8Array[outIdx++] = 240 | u >> 18;
outU8Array[outIdx++] = 128 | u >> 12 & 63;
outU8Array[outIdx++] = 128 | u >> 6 & 63;
outU8Array[outIdx++] = 128 | u & 63
}
}
outU8Array[outIdx] = 0;
return outIdx - startIdx
}
Используйте как:
stringToUTF8Array('abs', new Uint8Array(3), 0, 4);
Используйте приведенный ниже простой код для преобразования в
string
.
Предположим здесь
arraybuffer
как переменную binaryStr:
let text= <string>binaryStr;
а затем вы назначаете
text
государству.
var decoder = new TextDecoder ();
var string = decoder.decode (arrayBuffer);
См. https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode