在JavaScript中将字节数组转换为字符串

100

如何将字节数组转换为字符串?

我找到了这些函数来进行反向操作:

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

但是我该如何让这些函数双向工作呢?


你想要将字节数组转换为字符串,还是将位数组转换为字符串? - mcandre
请参阅适用于utf8数组的正确解决方案:Javascript中的Uint8Array转换为字符串 - Vadzim
16个回答

97
你需要将每个八位字节解析为数字,然后使用该值获取一个字符,类似于以下代码:
function bin2String(array) {
  var result = "";
  for (var i = 0; i < array.length; i++) {
    result += String.fromCharCode(parseInt(array[i], 2));
  }
  return result;
}

bin2String(["01100110", "01101111", "01101111"]); // "foo"

// Using your string2Bin function to test:
bin2String(string2Bin("hello world")) === "hello world";

编辑: 是的,你当前的 string2Bin 函数可以更简洁地编写:

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i).toString(2));
  }
  return result;
}

但是根据您提供的文档,我认为setBytesParameter方法期望blob数组包含十进制数,而不是一个位字符串,因此您可以编写类似于以下内容的代码:

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i));
  }
  return result;
}

function bin2String(array) {
  return String.fromCharCode.apply(String, array);
}

string2Bin('foo'); // [102, 111, 111]
bin2String(string2Bin('foo')) === 'foo'; // true

1
我需要进行这些转换的原因是因为我正在捕获签名并且必须将其转换为填充数据库中的BLOB字段。 问题是,虽然这两个函数起作用,但其他事情出了问题。主要问题是当我从数据库检索BLOB时,它会进入一个字节数组对象。然而,当我通过原始函数运行BLOB并将其写入数据库时,它不是字节数组对象。这可能是导致问题的原因。有什么想法吗? - user385579
4
在Safari中,对于非常长的字符串,“String.fromCharCode.apply(String, array)”是不安全的。这是由于JavaScriptCore存在问题,即函数不能超过65536个参数,否则会抛出RangeError错误。即使数组稍小一些,它也会使浏览器挂起。请参见https://bugs.webkit.org/show_bug.cgi?id=80797。 - Matthew
5
无法处理多字节的UTF-8字符,例如:bin2String([0xE2, 0x98, 0xB9]) - Brad Kent
在循环中使用 result += 操作符不会因为每次迭代都要重新分配内存而影响性能吗? - Vadzim
@BradKent string2Bin(bin2String([0xE2, 0x98, 0xB9])) == [0xE2, 0x98, 0xB9],因此不会失败。 - hldev
显示剩余3条评论

74

ES6更新

现在,字符串'foo'也等同于String.fromCharCode(...[102, 111, 111])

原始答案

只需将您的字节数组applyString.fromCharCode。例如:

String.fromCharCode.apply(null, [102, 111, 111])等同于'foo'。

MDN文档here

注意:仅适用于长度小于65535的数组 - MDN文档here


1
这已经在6年前被接受的答案证明过了。 - Preview
2
啊,确实,我错过了那一行。基本上我在寻找一个简短的一行代码,而忽略了那个长的已编辑的答案(可能太匆忙了)。 - Bogdan D
18
虽然重复了一遍,但简洁明了,比被接受的答案更好。 - Rich Apodaca

40

尝试新的文本编码 API:

// create an array view of some valid bytes
let bytesView = new Uint8Array([104, 101, 108, 108, 111]);

console.log(bytesView);

// convert bytes to string
// encoding can be specfied, defaults to utf-8
let str = new TextDecoder().decode(bytesView); 

console.log(str);

// convert string to bytes
// encoding can be specfied, defaults to utf-8
let bytes2 = new TextEncoder().encode(str);

// look, they're the same!
console.log(bytes2);
console.log(bytesView);


1
很遗憾,IE不支持它。 - Soul_man
如果您需要支持UTF-8和IE,您可以使用由MDN网站推荐的FastestSmallestTextEncoderDecoder polyfill - Rosberg Linhares
1
TextDecoder对于不可打印的ASCII字符会失败。测试new TextEncoder().encode(new TextDecoder().decode(new Uint8Array([255, 255, 255, 255])))不是[255, 255, 255, 255]。要将字节数组转换为字符串,可以使用String.fromCharCode(),然后进行反向操作。 - hldev
使用TextDecoder("UTF-16")来将字符串减半。在16位的JavaScript内存中,现在使用了上字节,因此它减少了内存的使用量。但是文件最好使用默认的"UTF-8",所以当我尝试下载时它变得更大了。 - Dan Froberg

18

这应该可以工作:

String.fromCharCode(...array);

或者

String.fromCodePoint(...array)

2
short and sweet ;) - abhisekp

7

那个string2Bin函数可以更加简洁,而且没有任何循环。

function string2Bin ( str ) {
    return str.split("").map( function( val ) { 
        return val.charCodeAt( 0 ); 
    } );
}

1
很想知道增加函数调用是否会使其变慢。 - jocull
39
它仍然有一个循环,只是它被隐藏在map()函数内部。 - Johannes Lumpe

5

字符串转换为字节数组: "FooBar".split('').map(c => c.charCodeAt(0));

字节数组转换为字符串: [102, 111, 111, 98, 97, 114].map(c => String.fromCharCode(c)).join('');


小心,這不受IE支持! - tedebus

4
即使我有点晚了,我认为与未来的用户分享我使用ES6实现的一行代码是有趣的。
根据您的环境和/或您将如何处理数据,有一件事我认为很重要,那就是保留完整的字节值。例如,(5).toString(2)将给您101,但实际上完整的二进制转换应该是00000101,这就是为什么您可能需要创建一个leftPad实现来用前导零填充字符串字节。但你可能根本不需要它,就像其他答案所示。
如果您运行以下代码片段,您将看到第一个输出是将abc字符串转换为字节数组,然后紧接着将该数组重新转换为其对应的字符串。

// For each byte in our array, retrieve the char code value of the binary value
const binArrayToString = array => array.map(byte => String.fromCharCode(parseInt(byte, 2))).join('')

// Basic left pad implementation to ensure string is on 8 bits
const leftPad = str => str.length < 8 ? (Array(8).join('0') + str).slice(-8) : str

// For each char of the string, get the int code and convert it to binary. Ensure 8 bits.
const stringToBinArray = str => str.split('').map(c => leftPad(c.charCodeAt().toString(2)))

const array = stringToBinArray('abc')

console.log(array)
console.log(binArrayToString(array))


4
我认为这样会更有效率:
function toBinString (arr) {
    var uarr = new Uint8Array(arr.map(function(x){return parseInt(x,2)}));
    var strings = [], chunksize = 0xffff;
    // There is a maximum stack size. We cannot call String.fromCharCode with as many arguments as we want
    for (var i=0; i*chunksize < uarr.length; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i*chunksize, (i+1)*chunksize)));
    }
    return strings.join('');
}

2
如果您的数组使用UTF-8编码,且由于IE不支持TextDecoder API而无法使用它,则可以按照以下两种方式之一进行处理:
  1. 使用FastestSmallestTextEncoderDecoder polyfill,该polyfill是Mozilla开发者网站推荐的;
  2. 使用MDN网站提供的此函数

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);


2
如果您正在使用 node.js,可以这样做:
yourByteArray.toString('base64');

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