Преобразование между UTF-8 ArrayBuffer и String

У меня есть ArrayBuffer, который содержит строку, кодированную с использованием UTF-8, и я не могу найти стандартный способ преобразования такого ArrayBuffer в JS String (который, как я понимаю, кодируется с использованием UTF-16).

Я видел этот код во многих местах, но я не вижу, как он будет работать с любыми кодовыми точками UTF-8, длина которых превышает 1 байт.

return String.fromCharCode.apply(null, new Uint8Array(data));

Аналогично, я не могу найти стандартный способ преобразования из String в кодированный UTF-8 ArrayBuffer.

Ответ 1

function stringToUint(string) {
    var string = btoa(unescape(encodeURIComponent(string))),
        charList = string.split(''),
        uintArray = [];
    for (var i = 0; i < charList.length; i++) {
        uintArray.push(charList[i].charCodeAt(0));
    }
    return new Uint8Array(uintArray);
}

function uintToString(uintArray) {
    var encodedString = String.fromCharCode.apply(null, uintArray),
        decodedString = decodeURIComponent(escape(atob(encodedString)));
    return decodedString;
}

Я сделал с некоторой помощью из Интернета эти маленькие функции, они должны решить ваши проблемы! Вот рабочий JSFiddle.

ИЗМЕНИТЬ

Поскольку источник Uint8Array является внешним, и вы не можете использовать atob, вам просто нужно его удалить (рабочая скрипка):

function uintToString(uintArray) {
    var encodedString = String.fromCharCode.apply(null, uintArray),
        decodedString = decodeURIComponent(escape(encodedString));
    return decodedString;
}

Ответ 2

Это должно работать:

// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt

/* utf.js - UTF-8 <=> UTF-16 convertion
 *
 * Copyright (C) 1999 Masanao Izumo <[email protected]>
 * Version: 1.0
 * LastModified: Dec 25 1999
 * This library is free.  You can redistribute it and/or modify it.
 */

function Utf8ArrayToStr(array) {
  var out, i, len, c;
  var char2, char3;

  out = "";
  len = array.length;
  i = 0;
  while (i < len) {
    c = array[i++];
    switch (c >> 4)
    { 
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
        // 0xxxxxxx
        out += String.fromCharCode(c);
        break;
      case 12: case 13:
        // 110x xxxx   10xx xxxx
        char2 = array[i++];
        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
        break;
      case 14:
        // 1110 xxxx  10xx xxxx  10xx xxxx
        char2 = array[i++];
        char3 = array[i++];
        out += String.fromCharCode(((c & 0x0F) << 12) |
                                   ((char2 & 0x3F) << 6) |
                                   ((char3 & 0x3F) << 0));
        break;
    }
  }    
  return out;
}

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

Проверьте демонстрацию JSFiddle.

Также смотрите связанные вопросы: здесь, здесь

Ответ 3

Использование TextEncoder и TextDecoder

var uint8array = new TextEncoder("utf-8").encode("Plain Text");
var string = new TextDecoder().decode(uint8array);
console.log(uint8array ,string )

Ответ 4

Там есть polyfill для Encoding на Github: text-encoding. Это легко для Node или браузера, и Readme советует следующее:

var uint8array = TextEncoder(encoding).encode(string);
var string = TextDecoder(encoding).decode(uint8array);

Если я помню, 'utf-8' - это encoding, который вам нужен, и, конечно же, вам нужно будет обернуть свой буфер:

var uint8array = new Uint8Array(utf8buffer);

Надеюсь, что это сработает так же хорошо для вас, как и для меня.

Ответ 5

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

function pad(n) {
    return n.length < 2 ? "0" + n : n;
}

var array = new Uint8Array(data);
var str = "";
for( var i = 0, len = array.length; i < len; ++i ) {
    str += ( "%" + pad(array[i].toString(16)))
}

str = decodeURIComponent(str);

Здесь представлена ​​демоверсия, которая декодирует 3-байтовый блок UTF-8: http://jsfiddle.net/Z9pQE/

Ответ 6

Методы readAsArrayBuffer и readAsText из объекта FileReader преобразуют Объект Blob для ArrayBuffer или для асинхронного DOMString.

Тип объекта Blob может быть создан из необработанного текста или массива байтов, например.

let blob = new Blob([text], { type: "text/plain" });

let reader = new FileReader();
reader.onload = event =>
{
    let buffer = event.target.result;
};
reader.readAsArrayBuffer(blob);

Я думаю, что лучше собрать это в обещании:

function textToByteArray(text)
{
    let blob = new Blob([text], { type: "text/plain" });
    let reader = new FileReader();
    let done = function() { };

    reader.onload = event =>
    {
        done(new Uint8Array(event.target.result));
    };
    reader.readAsArrayBuffer(blob);

    return { done: function(callback) { done = callback; } }
}

function byteArrayToText(bytes, encoding)
{
    let blob = new Blob([bytes], { type: "application/octet-stream" });
    let reader = new FileReader();
    let done = function() { };

    reader.onload = event =>
    {
        done(event.target.result);
    };

    if(encoding) { reader.readAsText(blob, encoding); } else { reader.readAsText(blob); }

    return { done: function(callback) { done = callback; } }
}

let text = "\uD83D\uDCA9 = \u2661";
textToByteArray(text).done(bytes =>
{
    console.log(bytes);
    byteArrayToText(bytes, 'UTF-8').done(text => 
    {
        console.log(text); // 💩 = ♡
    });
});

Ответ 7

Я столкнулся с той же проблемой, но мне нужно было время от времени анализировать/писать кодированные UTF8. Вот lib, который я только что сделал для решения этой проблемы https://github.com/nfroidure/UTF8.js.

Изменить: Кажется, что Mozilla что-то готовит для нас: StringView (https://developer.mozilla.org/en-US/docs/Code_snippets/StringView?redirectlocale=en-US&redirectslug=Web%2FJavaScript%2FTyped_arrays%2FStringView#encoding_values)

Ответ 8

Основная проблема программистов, ищущих преобразование байтового массива в строку, - кодировка (сжатие) символов Юникода в кодировке UTF-8. Этот код поможет вам:

var getString = function (strBytes) {

    var MAX_SIZE = 0x4000;
    var codeUnits = [];
    var highSurrogate;
    var lowSurrogate;
    var index = -1;

    var result = '';

    while (++index < strBytes.length) {
        var codePoint = Number(strBytes[index]);

        if (codePoint === (codePoint & 0x7F)) {

        } else if (0xF0 === (codePoint & 0xF0)) {
            codePoint ^= 0xF0;
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
        } else if (0xE0 === (codePoint & 0xE0)) {
            codePoint ^= 0xE0;
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
        } else if (0xC0 === (codePoint & 0xC0)) {
            codePoint ^= 0xC0;
            codePoint = (codePoint << 6) | (strBytes[++index] ^ 0x80);
        }

        if (!isFinite(codePoint) || codePoint < 0 || codePoint > 0x10FFFF || Math.floor(codePoint) != codePoint)
            throw RangeError('Invalid code point: ' + codePoint);

        if (codePoint <= 0xFFFF)
            codeUnits.push(codePoint);
        else {
            codePoint -= 0x10000;
            highSurrogate = (codePoint >> 10) | 0xD800;
            lowSurrogate = (codePoint % 0x400) | 0xDC00;
            codeUnits.push(highSurrogate, lowSurrogate);
        }
        if (index + 1 == strBytes.length || codeUnits.length > MAX_SIZE) {
            result += String.fromCharCode.apply(null, codeUnits);
            codeUnits.length = 0;
        }
    }

    return result;
}

Всего наилучшего !

Ответ 9

Для тех, кто работает в Node.js> = 0.1.99

Node.js предлагает встроенный модуль под названием StringDecoder который специально разработан для декодирования StringDecoder буферов в строки.

https://nodejs.org/api/string_decoder.html

По ссылке выше:

Модуль string_decoder предоставляет API для декодирования объектов Buffer в строки способом, который сохраняет закодированные многобайтовые символы UTF-8 и UTF-16.

Пример (заимствовано из вышеуказанных документов):


const { StringDecoder } = require('string_decoder');
const decoder = new StringDecoder('utf8');

const cent = Buffer.from([0xC2, 0xA2]);
console.log(decoder.write(cent)); // ¢

const euro = Buffer.from([0xE2, 0x82, 0xAC]);
console.log(decoder.write(euro)); // €