Uint8Array to string in Javascript
Asked Answered
E

21

280

I have some UTF-8 encoded data living in a range of Uint8Array elements in Javascript. Is there an efficient way to decode these out to a regular javascript string (I believe Javascript uses 16 bit Unicode)? I dont want to add one character at the time as the string concaternation would become to CPU intensive.

Expanded answered 20/1, 2012 at 5:7 Comment(3)
Not sure if it will work, but I use u8array.toString() when reading files from BrowserFS that expose Uint8Array object when you call fs.readFile.Sidoney
@Sidoney for me, toString on Uint8Array returns comma-separated numbers such as "91,50,48,49,57,45" (Chrome 79)Lueck
You can convert a Node.js Buffer to a JavaScript string using buffer.toString("utf8", start, end) where end = start + length. Unfortunately, browsers don't have Buffer. They only have Uint8Array. So, for browsers you can use new TextDecoder().decode(uint8array.subarray(start, end)). This will work in Node.js too because Buffer is a subclass of Uint8Array.Noteworthy
L
433

TextEncoder and TextDecoder from the Encoding standard, which is polyfilled by the stringencoding library, converts between strings and ArrayBuffers:

var uint8array = new TextEncoder().encode("someString");
var string = new TextDecoder().decode(uint8array);
Lactone answered 30/4, 2016 at 1:38 Comment(16)
For anyone lazy like me, npm install text-encoding, var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;. No thanks.Strigil
If I decode using this it will work for all browser right??Malayan
beware the npm text-encoding library, webpack bundle analyzer shows the library is HUGEPrecentor
Use a polyfill instead.Seline
this does the desired job for meFransiscafransisco
nodejs.org/api/string_decoder.html from the example: const { StringDecoder } = require('string_decoder'); const decoder = new StringDecoder('utf8'); const cent = Buffer.from([0xC2, 0xA2]); console.log(decoder.write(cent));Scientific
Note that Node.js added the TextEncoder/TextDecoder APIs in v11, so no need to install any extra packages if you only target current Node versions.Watterson
I think that nowadays the best polyfill is FastestSmallestTextEncoderDecoder, as recommended by the MDN website.Teetotum
I used this to decode Uint8Array into utf-8 string and I got a string that is shorter then Uint8Array (my Uint8Array was 259390 bytes long and the resulting string was 254463 bytes long). Anyone knows why?Typewriter
@Typewriter When you say the length of the string, do you mean the number of characters? This is what you'd expect if there are non-ascii characters (non-English letters etc). Ascii characters are 1 byte long in UTF-8; others are longer.Conjoin
I don't think this works for streaming data. If you're streaming you need to be able to handle the case where one of the characters gets divided between two chunks, but experiment suggests it gets this wrong (on Chromium v84 on Linux), even though the programming interface seems to have been designed for streaming.Conjoin
No, I tell a lie, you can stream it, but you have to do decoder.decode(buffer, { stream: true }), not just decoder.decode(buffer).Conjoin
supercool, look at what I did with your answer stackoverflow.com/questions/67855358/… the part $("#"+divid).append(new TextDecoder().decode(value));Portray
If the data array represents text, TextDecoder works just fine.Gutshall
Javascript String is of UTF-16, but TextDecoder().decode() give a longer UTF-8 string because 8 bits of the 16 bit character is not used.Rudnick
Use TextDecoder("UTF-16") to get half String.lengthRudnick
R
53

This should work:

// 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;
}

It's somewhat cleaner as the other solutions because it doesn't use any hacks nor depends on Browser JS functions, e.g. works also in other JS environments.

Check out the JSFiddle demo.

Also see the related questions: here and here

Revelry answered 13/3, 2014 at 8:41 Comment(5)
This seems kinda slow. But the only snippet in the universe I found that works. Good find+adoption!Lawrenson
I don't understand why this doesn't have more upvotes. It seems eminently sensible to crank through the UTF-8 convention for small snippets. Async Blob + Filereader works great for big texts as others have indicated.Doorn
The question was how to do this without string concatenationExpanded
Works great, except it doesn't handle 4+ byte sequences, e.g. fromUTF8Array([240,159,154,133]) turns out empty (while fromUTF8Array([226,152,131])→"☃")Defenestration
Why cases 8, 9, 10 and 11 are excluded? Any particular reason? And case 15 is also possible, right? 15(1111) will denote 4 bytes are used, isn't it?Stripteaser
D
51

Here's what I use:

var str = String.fromCharCode.apply(null, uint8Arr);
Denunciate answered 30/9, 2013 at 19:28 Comment(10)
From the doc, this does not seem to decode UTF8.Revelry
This will throw RangeError on bigger texts. "Maximum call stack size exceeded"Lawrenson
If you are converting large Uint8Arrays to binary strings and are getting RangeError, see the Uint8ToString function from https://mcmap.net/q/21032/-how-to-convert-uint8-array-to-base64-encoded-string.Endways
IE 11 throws SCRIPT28: Out of stack space when I feed it 300+k chars, or RangeError for Chrome 39. Firefox 33 is ok. 100+k runs ok with all three.Sielen
This does not produce the correct result from the example unicode characters on en.wikipedia.org/wiki/UTF-8. e.g. String.fromCharCode.apply(null, new Uint8Array([0xc2, 0xa2])) does not produce ¢.Lactone
This is just great. I spent three hours trying to find out how to decompress a folder on a Node JS Server with JSZip. Turns out that actual content, after you have decompressed the container is in uncompress.files[<fileName>]._data.compressedContent and you line extracted the String from the Uint8Array wonderfully. Thanks.Marine
Please remove this "answer" because it is blatantly incorrect. OP said he is decoding UTF-8 data and JavaScript strings are UTF-16 under the hood so while this may work for some situations due to overlap between UTF-8/16 characters, it will not always work and it is not a valid solution.Aquinas
Agreed, should not be storing Multi Byte data in Uint8.Passover
@SamClaus, it's true this doesn't handle utf-8, but does handle the general case of Uint8Array to string as stated in the title. If you need binary file data in a string or if your data is text but limited to ASCII this works. Many of the other solutions will alter binary data.Britannic
@arctic_hen7's solution Buffer.from(uint8arr.buffer).toString() is too faster. ~20x in my tests on Node.js v20. And @VincentScheib's solution (new TextDecoder().decode(uint8array)) is faster than both!Colin
W
36

In NodeJS, we have Buffers available, and string conversion with them is really easy. Better, it's easy to convert a Uint8Array to a Buffer. Try this code, it's worked for me in Node for basically any conversion involving Uint8Arrays:

let str = Buffer.from(uint8arr.buffer).toString();

We're just extracting the ArrayBuffer from the Uint8Array and then converting that to a proper NodeJS Buffer. Then we convert the Buffer to a string (you can throw in a hex or base64 encoding if you want).

If we want to convert back to a Uint8Array from a string, then we'd do this:

let uint8arr = new Uint8Array(Buffer.from(str));

Be aware that if you declared an encoding like base64 when converting to a string, then you'd have to use Buffer.from(str, "base64") if you used base64, or whatever other encoding you used.

This will not work in the browser without a module! NodeJS Buffers just don't exist in the browser, so this method won't work unless you add Buffer functionality to the browser. That's actually pretty easy to do though, just use a module like this, which is both small and fast!

Woodborer answered 22/5, 2020 at 22:40 Comment(0)
B
27

In Node "Buffer instances are also Uint8Array instances", so buf.toString() works in this case.

Baptista answered 19/6, 2017 at 22:14 Comment(6)
Works great for me. And so simple ! But in fact Uint8Array have toString() method.Hardej
Simple and elegant, was not aware Buffer is also Uint8Array. Thanks!Tache
@Hardej On the browser side, Uint8Array.toString() will not compile a utf-8 string, it will list the numeric values in the array. So if what you have is a Uint8Array from another source that does not happen to also be a Buffer, you will need to create one to do the magic: Buffer.from(uint8array).toString('utf-8')Hephaestus
How about “Buffer.prototype.toString.call(uint8array, ‘utf8’)” to avoid creating a new buffer instance.Dermoid
This does not work in Chrome. Buffer is nodejs only.Godfearing
This answer is confusing because the OP has a Uint8Array to start with. All Buffers are Uint8Arrays, but not all Uint8Arrays are buffers. Joachim has the correct answer but unfortunately Buffer.from creates a copy in this case, rather than a view, according to Node's documentation.Abb
T
21

Found in one of the Chrome sample applications, although this is meant for larger blocks of data where you're okay with an asynchronous conversion.

/**
 * Converts an array buffer to a string
 *
 * @private
 * @param {ArrayBuffer} buf The buffer to convert
 * @param {Function} callback The function to call when conversion is complete
 */
function _arrayBufferToString(buf, callback) {
  var bb = new Blob([new Uint8Array(buf)]);
  var f = new FileReader();
  f.onload = function(e) {
    callback(e.target.result);
  };
  f.readAsText(bb);
}
Tamratamsky answered 29/12, 2012 at 5:19 Comment(1)
As you said, this would perform terribly unless the buffer to convert is really really huge. The synchronous UTF-8 to wchar converstion of a simple string (say 10-40 bytes) implemented in, say, V8 should be much less than a microsecond whereas I would guess that your code would require a hundreds times that. Thanks all the same.Expanded
E
18

The solution given by Albert works well as long as the provided function is invoked infrequently and is only used for arrays of modest size, otherwise it is egregiously inefficient. Here is an enhanced vanilla JavaScript solution that works for both Node and browsers and has the following advantages:

• Works efficiently for all octet array sizes

• Generates no intermediate throw-away strings

• Supports 4-byte characters on modern JS engines (otherwise "?" is substituted)

var utf8ArrayToStr = (function () {
    var charCache = new Array(128);  // Preallocate the cache for the common single byte chars
    var charFromCodePt = String.fromCodePoint || String.fromCharCode;
    var result = [];

    return function (array) {
        var codePt, byte1;
        var buffLen = array.length;

        result.length = 0;

        for (var i = 0; i < buffLen;) {
            byte1 = array[i++];

            if (byte1 <= 0x7F) {
                codePt = byte1;
            } else if (byte1 <= 0xDF) {
                codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
            } else if (byte1 <= 0xEF) {
                codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
            } else if (String.fromCodePoint) {
                codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
            } else {
                codePt = 63;    // Cannot convert four byte code points, so use "?" instead
                i += 3;
            }

            result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
        }

        return result.join('');
    };
})();
Edy answered 23/1, 2017 at 2:13 Comment(2)
Best solution here, as it also handles 4-byte-characters (e.g. emojis) Thank you!Poinsettia
and what is the inverse of this?Hellenhellene
A
9

Uint8Array to String

let str = Buffer.from(key.secretKey).toString('base64');

String to Uint8Array

let uint8arr = new Uint8Array(Buffer.from(data,'base64')); 
Austerity answered 23/9, 2021 at 6:0 Comment(0)
B
7

Do what @Sudhir said, and then to get a String out of the comma seperated list of numbers use:

for (var i=0; i<unitArr.byteLength; i++) {
            myString += String.fromCharCode(unitArr[i])
        }

This will give you the string you want, if it's still relevant

Barren answered 3/4, 2012 at 11:59 Comment(6)
Sorry, haven't noticed the last sentense in which you said you don't want to add one character at a time. Hope this helps others who doesn't have a problem with CPU usage however.Barren
This does not do UTF8 decoding.Revelry
Even shorter: String.fromCharCode.apply(null, unitArr);. As mentioned, it doesn't handle UTF8 encoding, but sometimes this is simple enough if you only need ASCII support but don't have access to TextEncoder/TextDecoder.Pilferage
The answer mentions a @Sudhir but I searched the page and found now such answer. So would be better to inline whatever he saidYoruba
This will have terrible performance on longer strings. Don't use the + operator on strings.Sustentation
@Sustentation Modern JavaScript engines are optimized for string concatenation operators. Reference Also, MDN says, "It is strongly recommended that the assignment operators (+, +=) are used instead of the concat() method."Nice
H
6

I was frustrated to see that people were not showing how to go both ways or showing that things work on none trivial UTF8 strings. I found a post on codereview.stackexchange.com that has some code that works well. I used it to turn ancient runes into bytes, to test some crypo on the bytes, then convert things back into a string. The working code is on github here. I renamed the methods for clarity:

// https://codereview.stackexchange.com/a/3589/75693
function bytesToSring(bytes) {
    var chars = [];
    for(var i = 0, n = bytes.length; i < n;) {
        chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
    }
    return String.fromCharCode.apply(null, chars);
}

// https://codereview.stackexchange.com/a/3589/75693
function stringToBytes(str) {
    var bytes = [];
    for(var i = 0, n = str.length; i < n; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char >>> 8, char & 0xFF);
    }
    return bytes;
}

The unit test uses this UTF-8 string:

    // http://kermitproject.org/utf8.html
    // From the Anglo-Saxon Rune Poem (Rune version) 
    const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`;

Note that the string length is only 117 characters but the byte length, when encoded, is 234.

If I uncomment the console.log lines I can see that the string that is decoded is the same string that was encoded (with the bytes passed through Shamir's secret sharing algorithm!):

unit test that demos encoding and decoding

Hellenhellene answered 19/6, 2019 at 7:12 Comment(5)
String.fromCharCode.apply(null, chars) will error if chars is too big.Slaw
is that everywhere or just some browsers and is it documented at all?Hellenhellene
e.g. here developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… But beware: by using apply this way, you run the risk of exceeding the JavaScript engine's argument length limit. The consequences of applying a function with too many arguments (that is, more than tens of thousands of arguments) varies across engines. (The JavaScriptCore engine has hard-coded argument limit of 65536.Slaw
thanks. in my case i was doing crypto over smallish strings so not a problem. do you have a fix for long strings? :-)Hellenhellene
solution is to batch in 64k chars.Slaw
T
4

If you can't use the TextDecoder API because it is not supported on IE:

  1. You can use the FastestSmallestTextEncoderDecoder polyfill recommended by the Mozilla Developer Network website;
  2. You can use this function also provided at the MDN website:

function utf8ArrayToString(aBytes) {
    var sView = "";
    
    for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
        nPart = aBytes[nIdx];
        
        sView += String.fromCharCode(
            nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
                /* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
                (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
                (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
                (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
                (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
                (nPart - 192 << 6) + aBytes[++nIdx] - 128
            : /* nPart < 127 ? */ /* one byte */
                nPart
        );
    }
    
    return sView;
}

let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);

// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);
Teetotum answered 5/12, 2019 at 2:41 Comment(0)
O
4

I'm using this function, which works for me:

function uint8ArrayToBase64(data) {
    return btoa(Array.from(data).map((c) => String.fromCharCode(c)).join(''));
}
Olinger answered 26/3, 2022 at 8:32 Comment(0)
E
3

Try these functions,

var JsonToArray = function(json)
{
    var str = JSON.stringify(json, null, 0);
    var ret = new Uint8Array(str.length);
    for (var i = 0; i < str.length; i++) {
        ret[i] = str.charCodeAt(i);
    }
    return ret
};

var binArrayToJson = function(binArray)
{
    var str = "";
    for (var i = 0; i < binArray.length; i++) {
        str += String.fromCharCode(parseInt(binArray[i]));
    }
    return JSON.parse(str)
}

source: https://gist.github.com/tomfa/706d10fed78c497731ac, kudos to Tomfa

Espagnole answered 22/6, 2018 at 16:20 Comment(2)
It will also work if you don't have the parseInt() in binArrayToJson.Nahshunn
Won't these break if the uint8 array results in invalid utf-8 sequences?Forefinger
M
3

For ES6 and UTF8 string

decodeURIComponent(escape(String.fromCharCode(...uint8arrData)))
Matthias answered 17/12, 2021 at 8:53 Comment(0)
T
1

By far the easiest way that has worked for me is:


//1. Create or fetch the Uint8Array to use in the example
const bufferArray = new Uint8Array([10, 10, 10])

//2. Turn the Uint8Array into a regular array
const array = Array.from(bufferArray);

//3. Stringify it (option A)
JSON.stringify(array);


//3. Stringify it (option B: uses @serdarsenay code snippet to decode each item in array)
let binArrayToString = function(binArray) {
    let str = "";
    for (let i = 0; i < binArray.length; i++) {        
        str += String.fromCharCode(parseInt(binArray[i]));
    }
    return str;
}

binArrayToString(array);
Tommie answered 7/4, 2021 at 21:25 Comment(1)
Small note on this: the binArrayToString function Franco mentions is much faster than using the Array map functionality (Array.from(data).map((c) => String.fromCharCode(c)).join('')) I see in many discussions.Nahshunn
C
1

In my case, I ended up in this question during the migration from aws-sdk (v2) to @aws-sdk/client-lambda (v3).

I am invoking another lambda from my lambda as the following:

lambdaClient.send(new InvokeCommand(invocationRequest))

and the response type is InvokeCommandOutput.

For accessing the payload inside the object (type of Uint8ArrayBlobAdapter(225) [Uint8Array]) I have figured that I can use this adapter's built-in method: transformToString.

Full code:

JSON.parse(params.response.Payload?.transformToString() || '');

Alternatively, you can use:

JSON.parse(new TextDecoder('utf-8').decode(response.Payload))

or

JSON.parse(new Buffer.from(response.Payload).toString())
Cartier answered 21/11, 2023 at 18:39 Comment(0)
P
0
class UTF8{
static encode(str:string){return new UTF8().encode(str)}
static decode(data:Uint8Array){return new UTF8().decode(data)}

private EOF_byte:number = -1;
private EOF_code_point:number = -1;
private encoderError(code_point) {
    console.error("UTF8 encoderError",code_point)
}
private decoderError(fatal, opt_code_point?):number {
    if (fatal) console.error("UTF8 decoderError",opt_code_point)
    return opt_code_point || 0xFFFD;
}
private inRange(a:number, min:number, max:number) {
    return min <= a && a <= max;
}
private div(n:number, d:number) {
    return Math.floor(n / d);
}
private stringToCodePoints(string:string) {
    /** @type {Array.<number>} */
    let cps = [];
    // Based on http://www.w3.org/TR/WebIDL/#idl-DOMString
    let i = 0, n = string.length;
    while (i < string.length) {
        let c = string.charCodeAt(i);
        if (!this.inRange(c, 0xD800, 0xDFFF)) {
            cps.push(c);
        } else if (this.inRange(c, 0xDC00, 0xDFFF)) {
            cps.push(0xFFFD);
        } else { // (inRange(c, 0xD800, 0xDBFF))
            if (i == n - 1) {
                cps.push(0xFFFD);
            } else {
                let d = string.charCodeAt(i + 1);
                if (this.inRange(d, 0xDC00, 0xDFFF)) {
                    let a = c & 0x3FF;
                    let b = d & 0x3FF;
                    i += 1;
                    cps.push(0x10000 + (a << 10) + b);
                } else {
                    cps.push(0xFFFD);
                }
            }
        }
        i += 1;
    }
    return cps;
}

private encode(str:string):Uint8Array {
    let pos:number = 0;
    let codePoints = this.stringToCodePoints(str);
    let outputBytes = [];

    while (codePoints.length > pos) {
        let code_point:number = codePoints[pos++];

        if (this.inRange(code_point, 0xD800, 0xDFFF)) {
            this.encoderError(code_point);
        }
        else if (this.inRange(code_point, 0x0000, 0x007f)) {
            outputBytes.push(code_point);
        } else {
            let count = 0, offset = 0;
            if (this.inRange(code_point, 0x0080, 0x07FF)) {
                count = 1;
                offset = 0xC0;
            } else if (this.inRange(code_point, 0x0800, 0xFFFF)) {
                count = 2;
                offset = 0xE0;
            } else if (this.inRange(code_point, 0x10000, 0x10FFFF)) {
                count = 3;
                offset = 0xF0;
            }

            outputBytes.push(this.div(code_point, Math.pow(64, count)) + offset);

            while (count > 0) {
                let temp = this.div(code_point, Math.pow(64, count - 1));
                outputBytes.push(0x80 + (temp % 64));
                count -= 1;
            }
        }
    }
    return new Uint8Array(outputBytes);
}

private decode(data:Uint8Array):string {
    let fatal:boolean = false;
    let pos:number = 0;
    let result:string = "";
    let code_point:number;
    let utf8_code_point = 0;
    let utf8_bytes_needed = 0;
    let utf8_bytes_seen = 0;
    let utf8_lower_boundary = 0;

    while (data.length > pos) {
        let _byte = data[pos++];

        if (_byte == this.EOF_byte) {
            if (utf8_bytes_needed != 0) {
                code_point = this.decoderError(fatal);
            } else {
                code_point = this.EOF_code_point;
            }
        } else {
            if (utf8_bytes_needed == 0) {
                if (this.inRange(_byte, 0x00, 0x7F)) {
                    code_point = _byte;
                } else {
                    if (this.inRange(_byte, 0xC2, 0xDF)) {
                        utf8_bytes_needed = 1;
                        utf8_lower_boundary = 0x80;
                        utf8_code_point = _byte - 0xC0;
                    } else if (this.inRange(_byte, 0xE0, 0xEF)) {
                        utf8_bytes_needed = 2;
                        utf8_lower_boundary = 0x800;
                        utf8_code_point = _byte - 0xE0;
                    } else if (this.inRange(_byte, 0xF0, 0xF4)) {
                        utf8_bytes_needed = 3;
                        utf8_lower_boundary = 0x10000;
                        utf8_code_point = _byte - 0xF0;
                    } else {
                        this.decoderError(fatal);
                    }
                    utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
                    code_point = null;
                }
            } else if (!this.inRange(_byte, 0x80, 0xBF)) {
                utf8_code_point = 0;
                utf8_bytes_needed = 0;
                utf8_bytes_seen = 0;
                utf8_lower_boundary = 0;
                pos--;
                code_point = this.decoderError(fatal, _byte);
            } else {
                utf8_bytes_seen += 1;
                utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);

                if (utf8_bytes_seen !== utf8_bytes_needed) {
                    code_point = null;
                } else {
                    let cp = utf8_code_point;
                    let lower_boundary = utf8_lower_boundary;
                    utf8_code_point = 0;
                    utf8_bytes_needed = 0;
                    utf8_bytes_seen = 0;
                    utf8_lower_boundary = 0;
                    if (this.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) {
                        code_point = cp;
                    } else {
                        code_point = this.decoderError(fatal, _byte);
                    }
                }

            }
        }
        //Decode string
        if (code_point !== null && code_point !== this.EOF_code_point) {
            if (code_point <= 0xFFFF) {
                if (code_point > 0)result += String.fromCharCode(code_point);
            } else {
                code_point -= 0x10000;
                result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
                result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
            }
        }
    }
    return result;
}

`

Pseudo answered 2/8, 2017 at 6:42 Comment(1)
Add some description to answer. @PseudoFondness
K
0

Using base64 as the encoding format works quite well. This is how it was implemented for passing secrets via urls in Firefox Send. You will need the base64-js package. These are the functions from the Send source code:

const b64 = require("base64-js")

function arrayToB64(array) {
  return b64.fromByteArray(array).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
}

function b64ToArray(str) {
  return b64.toByteArray(str + "===".slice((str.length + 3) % 4))
}
Kernan answered 6/6, 2021 at 16:27 Comment(0)
A
0

With vanilla, browser side, recording from microphone, base64 functions worked for me (I had to implement an audio sending function to a chat).

      const ui8a =  new Uint8Array(e.target.result);
      const string = btoa(ui8a);
      const ui8a_2 = atob(string).split(',');

Full code now. Thanks to Bryan Jennings & [email protected] for the code.

https://medium.com/@bryanjenningz/how-to-record-and-play-audio-in-javascript-faa1b2b3e49b

https://www.py4u.net/discuss/282499

index.html

<html>
  <head>
    <title>Record Audio Test</title>
    <meta name="encoding" charset="utf-8" />
  </head>
  <body>
    <h1>Audio Recording Test</h1>
    <script src="index.js"></script>
    <button id="action" onclick="start()">Start</button>
    <button id="stop" onclick="stop()">Stop</button>
    <button id="play" onclick="play()">Listen</button>
  </body>
</html>

index.js:

const recordAudio = () =>
  new Promise(async resolve => {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    const mediaRecorder = new MediaRecorder(stream);
    const audioChunks = [];

    mediaRecorder.addEventListener("dataavailable", event => {
      audioChunks.push(event.data);
    });

    const start = () => mediaRecorder.start();

    const stop = () =>
      new Promise(resolve => {
        mediaRecorder.addEventListener("stop", () => {
          const audioBlob = new Blob(audioChunks);
          const audioUrl = URL.createObjectURL(audioBlob);
          const audio = new Audio(audioUrl);
          const play = () => audio.play();
          resolve({ audioBlob, audioUrl, play });
        });

        mediaRecorder.stop();
      });

    resolve({ start, stop });
  });

let recorder = null;
let audio = null;
const sleep = time => new Promise(resolve => setTimeout(resolve, time));

const start = async () => {
  recorder = await recordAudio();
  recorder.start();
}

const stop = async () => {
  audio = await recorder.stop();
  read(audio.audioUrl);
}

const play = ()=> {
  audio.play();
}

const read = (blobUrl)=> {

  var xhr = new XMLHttpRequest;
  xhr.responseType = 'blob';
  
  xhr.onload = function() {
      var recoveredBlob = xhr.response;
      const reader = new FileReader();
      // This fires after the blob has been read/loaded.
      reader.addEventListener('loadend', (e) => {

          const ui8a =  new Uint8Array(e.target.result);
          const string = btoa(ui8a);
          const ui8a_2 = atob(string).split(',');
          
          playByteArray(ui8a_2);
      });
      // Start reading the blob as text.
      reader.readAsArrayBuffer(recoveredBlob);
  };
  // get the blob through blob url 
  xhr.open('GET', blobUrl);
  xhr.send();
}

window.onload = init;
var context;    // Audio context
var buf;        // Audio buffer

function init() {
  if (!window.AudioContext) {
      if (!window.webkitAudioContext) {
          alert("Your browser does not support any AudioContext and cannot play back this audio.");
          return;
      }
        window.AudioContext = window.webkitAudioContext;
    }

    context = new AudioContext();
}

function playByteArray(byteArray) {

    var arrayBuffer = new ArrayBuffer(byteArray.length);
    var bufferView = new Uint8Array(arrayBuffer);
    for (i = 0; i < byteArray.length; i++) {
      bufferView[i] = byteArray[i];
    }

    context.decodeAudioData(arrayBuffer, function(buffer) {
        buf = buffer;
        play2();
    });
}

// Play the loaded file
function play2() {
    // Create a source node from the buffer
    var source = context.createBufferSource();
    source.buffer = buf;
    // Connect to the final output node (the speakers)
    source.connect(context.destination);
    // Play immediately
    source.start(0);
}
Asquint answered 10/10, 2021 at 14:46 Comment(0)
B
-2

var decodedString = decodeURIComponent(escape(String.fromCharCode(...new Uint8Array(err)))); var obj = JSON.parse(decodedString);

Borrero answered 27/7, 2022 at 12:22 Comment(0)
U
-3

I am using this Typescript snippet:

function UInt8ArrayToString(uInt8Array: Uint8Array): string
{
    var s: string = "[";
    for(var i: number = 0; i < uInt8Array.byteLength; i++)
    {
        if( i > 0 )
            s += ", ";
        s += uInt8Array[i];
    }
    s += "]";
    return s;
}

Remove the type annotations if you need the JavaScript version. Hope this helps!

Unlive answered 10/1, 2014 at 21:6 Comment(1)
The OP asked to not add one char at a time. Also, he does not want to display it as a string-representation of list but rather just as a string. Also, this doesn't convert the chars to string but displays its number.Revelry

© 2022 - 2024 — McMap. All rights reserved.