如何从ArrayBuffer中获取二进制字符串?

27

如何在JavaScript中从ArrayBuffer获取二进制字符串?

我不想编码字节,只需将其作为字符串获取二进制表示。

提前感谢!


1
我并不完全理解你实际想要完成的转换,你能否给出进一步的解释以澄清你的需求? - Xotic750
5个回答

20
以下代码可以始终将一个ArrayBuffer转换为一个String,并再次转换回去,而不会丢失或添加任何额外的字节。
function ArrayBufferToString(buffer) {
    return BinaryToString(String.fromCharCode.apply(null, Array.prototype.slice.apply(new Uint8Array(buffer))));
}

function StringToArrayBuffer(string) {
    return StringToUint8Array(string).buffer;
}

function BinaryToString(binary) {
    var error;

    try {
        return decodeURIComponent(escape(binary));
    } catch (_error) {
        error = _error;
        if (error instanceof URIError) {
            return binary;
        } else {
            throw error;
        }
    }
}

function StringToBinary(string) {
    var chars, code, i, isUCS2, len, _i;

    len = string.length;
    chars = [];
    isUCS2 = false;
    for (i = _i = 0; 0 <= len ? _i < len : _i > len; i = 0 <= len ? ++_i : --_i) {
        code = String.prototype.charCodeAt.call(string, i);
        if (code > 255) {
            isUCS2 = true;
            chars = null;
            break;
        } else {
            chars.push(code);
        }
    }
    if (isUCS2 === true) {
        return unescape(encodeURIComponent(string));
    } else {
        return String.fromCharCode.apply(null, Array.prototype.slice.apply(chars));
    }
}

function StringToUint8Array(string) {
    var binary, binLen, buffer, chars, i, _i;
    binary = StringToBinary(string);
    binLen = binary.length;
    buffer = new ArrayBuffer(binLen);
    chars  = new Uint8Array(buffer);
    for (i = _i = 0; 0 <= binLen ? _i < binLen : _i > binLen; i = 0 <= binLen ? ++_i : --_i) {
        chars[i] = String.prototype.charCodeAt.call(binary, i);
    }
    return chars;
}

我通过在这个 jsfiddle 上往返测试以下值来进行测试:http://jsfiddle.net/potatosalad/jrdLV/

(String) "abc" -> (ArrayBuffer) -> (String) "abc"
(String) "aΩc" -> (ArrayBuffer) -> (String) "aΩc"
(Uint8Array) [0,1,255] -> (ArrayBuffer) -> (String) -> (Uint8Array) [0,1,255]
(Uint16Array) [0,1,256,65535] -> (ArrayBuffer) -> (String) -> (Uint16Array) [0,1,256,65535]
(Uint32Array) [0,1,256,65536,4294967295] -> (ArrayBuffer) -> (String) -> (Uint32Array) [0,1,256,65536,4294967295]

1
谢谢。fileReader.readAsArrayBuffer 和 fileReader.readAsBinaryString 返回的编码是什么? - Mike
@aung readAsBinaryString 只是基于将单个字节转换为 0 和 1 的字符串。 没有“编码”。 readAsArrayBuffer 使用您指定的任何掩码(即 Uint8、Uint16、Uint32 等)。 - vapcguy
ArrayBufferToString() 似乎无法正确地将 ArrayBuffer / Uint8Array 转换为二进制字符串。使用 PNG 文件进行测试,会添加一些字节,有些字节会改变。大多数是正确的,但是我做错了什么吗?我使用 FileReader.readAsArrayBuffer() 读取 PNG 文件,然后将其发送到 ArrayBufferToString()。 - StanE

14

随着近年来JavaScript的更新,这变得更加简单了——以下是一种将Uint8Array转换为二进制编码字符串的一行代码方法:

const toBinString = (bytes) =>
  bytes.reduce((str, byte) => str + byte.toString(2).padStart(8, '0'), '');

例子:

console.log(toBinString(Uint8Array.from([42, 100, 255, 0])))
// => '00101010011001001111111100000000'
如果你使用的是ArrayBuffer,需要创建一个Uint8Array“视图”将缓冲区传递给此方法:
const view = new Uint8Array(myArrayBuffer);
console.log(toBinString(view));

来源: Libauth库(binToBinString 方法


3
这将从一个类型化数组中提取二进制字符串。
var bitsPerByte = 8;
var array = new Uint8Array([0, 50, 100, 170, 200, 255]);
var string = "";

function repeat(str, num) {
    if (str.length === 0 || num <= 1) {
        if (num === 1) {
            return str;
        }

        return '';
    }

    var result = '',
        pattern = str;

    while (num > 0) {
        if (num & 1) {
            result += pattern;
        }

        num >>= 1;
        pattern += pattern;
    }

    return result;
}

function lpad(obj, str, num) {
    return repeat(str, num - obj.length) + obj;
}

Array.prototype.forEach.call(array, function (element) {
    string += lpad(element.toString(2), "0", bitsPerByte);
});

console.log(string);

输出为

000000000011001001100100101010101100100011111111

jsfiddle

或者也许你是在问这个?

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}

注意,以这种方式使用 apply 意味着您可能会达到参数限制(大约 16000 个元素),然后您将不得不遍历数组元素。
html5rocks 上。

这里似乎授予了一些代码解释。 - Muhammad Umer

1
我知道这是一个非常老的问题,但我想添加一个可能的情况/解决方案,因为我通过搜索找到了它:
我想要一个async/await版本的FileReader.readAsBinaryString(),因为我是个善变的人,回调接口让我感到烦恼:
// Suppose `file` exists

let fr = new FileReader()
let string1

fr.onload = ({ target: { result }}) => string1 = result
fr.readAsBinaryString(file)

console.log(string1)

此外,MDN表示FileReader.readAsBinaryString()仅用于向后兼容,建议使用FileReader.readAsArrayBuffer()
现在Blob/File有新的方法可提供文本ArrayBuffer,但没有二进制字符串。遵循MDN的建议,我们可以使用新的方法从ArrayBuffer转换为二进制字符串,确实可以实现:
// Again, suppose `file` exists

let ab, bufferToString, string2

bufferToString = (buffer) => {
    const bytes = new Uint8Array(buffer)
    return bytes.reduce((string, byte) => (string + String.fromCharCode(byte)), "")
}

ab = await file.arrayBuffer()
string2 = bufferToString(ab)

console.log(string2)

假设这是在同一个浏览器控制台中完成的:
string1 === string2 // true

无论这是智慧还是愚蠢,都留给读者自行判断。

请注意,readAsBinaryString现已弃用。 - Eduardo Procópio Gomez
1
@EduardoProcópioGomez 我注意到了。这已经在答案中了,而且实际上也是我得出解决方案的全部原因。 - flanger001

-1
function string2Bin(s) {
  var b = new Array();
  var last = s.length;
  for (var i = 0; i < last; i++) {
    var d = s.charCodeAt(i);
    if (d < 128)
      b[i] = dec2Bin(d);
    else {
      var c = s.charAt(i);
      alert(c + ' is NOT an ASCII character');
      b[i] = -1;
    }
  }
  return b;
}

function dec2Bin(d) {
  var b = '';
  for (var i = 0; i < 8; i++) {
    b = (d%2) + b;
    d = Math.floor(d/2);
  }
  return b;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接