在Javascript中在base-2(二进制),base-16(十六进制)和base-64之间进行转换

时间:2016-09-28 00:00:57

标签: javascript encoding binary hex base64

在Javascript中将ASCII字符串转换为十六进制和base-64的转换非常简单。但是,处理二进制和UTF编码的字符串会引发误解。

Javascript的内置atob()和btoa()函数不适用于UTF编码的字符串,这是来自声明UTF-8字符集的HTML文档中的元素(例如输入)的字符串的主要问题。此外,似乎base-64只能使用已经是ASCII编码的十六进制的字符串直接编码,没有直接的方法将二进制字符串(ASCII或UTF-8编码)转换为base-64。 / p>

为了进一步加剧这个问题,似乎SO和其他地方发布的几乎所有问题都假设“二进制字符串”等同于以十六进制编码字符串表示的二进制数据,而不是由基数组成的字符串 - 2个数字。

鉴于UTF-8或ASCII编码的字符串由二进制,十六进制或base-64字符组成,您如何在三者之间进行转换?

1 个答案:

答案 0 :(得分:0)

这是我到目前为止所提出的答案。我还没有考虑消除hex⇌base-64的中间步骤,所以这些函数涉及到二进制的转换。

// Binary → Hex
binToHex = (value) => {
  let hexString = '';

  for (let i=0; i < (value.length)/4; i++) {
    let piece = value.substr(4*i, 4);
    hexString += parseInt(piece, 2).toString(16);
  }

  return hexString;
}

// Binary → Base-64
binToB64 = (value) => {
    let arrayBuffer = new ArrayBuffer(Math.ceil(value.length/8))
        ,decArray 
        ,uint8DecArray

    bitsToDecArray = (bits) => {
        let decArray = [];

        for (let i=0; i< Math.ceil(value.length/8); i++) {
            let length = 8*i+8 > value.length ? value.length - 8*i : 8
                ,bin = value.substr(8*i, length)

            decArray.push(parseInt(bin, 2).toString(10));
        }

        return decArray;
    }

    decArray = bitsToDecArray(value);
    uint8DecArray = new Uint8Array(decArray);

    // From http://stackoverflow.com/a/7372816/4111381
    base64ArrayBuffer = (arrayBuffer) => {
        let base64    = ''
            ,encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
            ,bytes         = new Uint8Array(arrayBuffer)
            ,byteLength    = bytes.byteLength
            ,byteRemainder = byteLength % 3
            ,mainLength    = byteLength - byteRemainder
            ,a, b, c, d
            ,chunk

        // Main loop deals with bytes in chunks of 3
        for (var i = 0; i < mainLength; i = i + 3) {
            // Combine the three bytes into a single integer
            chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]

            // Use bitmasks to extract 6-bit segments from the triplet
            a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
            b = (chunk & 258048)   >> 12 // 258048   = (2^6 - 1) << 12
            c = (chunk & 4032)     >>  6 // 4032     = (2^6 - 1) << 6
            d = chunk & 63               // 63       = 2^6 - 1

            // Convert the raw binary segments to the appropriate ASCII encoding
            base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
        }

        // Deal with the remaining bytes and padding
        if (byteRemainder == 1) {
            chunk = bytes[mainLength]

            a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2

            // Set the 4 least significant bits to zero
            b = (chunk & 3)   << 4 // 3   = 2^2 - 1

            base64 += encodings[a] + encodings[b] + '=='
        } else if (byteRemainder == 2) {
            chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]

            a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
            b = (chunk & 1008)  >>  4 // 1008  = (2^6 - 1) << 4

            // Set the 2 least significant bits to zero
            c = (chunk & 15)    <<  2 // 15    = 2^4 - 1

            base64 += encodings[a] + encodings[b] + encodings[c] + '='
        }

        return base64
    }

    return base64ArrayBuffer(uint8DecArray);
}

// Hex → Bin
hexToBin = (value) => {
    let binString = '';

    for (let i=0; i < value.length; i++) {
        let bin = parseInt(value[i], 16).toString(2);
        binString += ('0000' + bin).slice(-4);
    }

    return binString;
}

// Hex → Base-64
hexToB64 = (value) => {
    return binToB64(hexToBin(value));
}

// Base-64 → Binary
b64ToBin = (value) => {
    let bitString = ''
        ,base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

    for(let i = 0; i < value.length; i+=4) {
        let segment = '';

        for (let j = 0; j < 4; j++) {
            console.log(i+j)
            if (value[i+j] != '=') {
                let bin = base64chars.indexOf(value[i+j]).toString(2)
                segment += ('000000' + bin).slice(-6);
            }
            else segment += '000000';
        }
        bitString += segment;
    }

    // Strip ending null bytes
    while (bitString.endsWith('00000000')) {
        bitString = bitString.substr(0, bitString.length-8);
    }

    return bitString;
}

// Base-64 → Hex
b64ToHex = (value) => {
    return binToHex(b64ToBin(value));
}