【问题标题】:How to convert UTF8 string to byte array?如何将 UTF8 字符串转换为字节数组?
【发布时间】:2013-09-14 18:46:13
【问题描述】:

.charCodeAt 函数返回字符的 unicode 代码。但我想改为获取字节数组。我知道,如果 charcode 超过 127,那么字符存储在两个或多个字节中。

var arr=[];
for(var i=0; i<str.length; i++) {
    arr.push(str.charCodeAt(i))
}

【问题讨论】:

    标签: javascript utf-8


    【解决方案1】:

    Encoding API 可让您轻松编码和解码 UTF-8(使用类型化数组):

    var encoded = new TextEncoder().encode("Γεια σου κόσμε");
    var decoded = new TextDecoder("utf-8").decode(encoded);
        
    console.log(encoded, decoded);
    

    浏览器支持isn't too bad,并且有一个polyfill 应该可以在IE11 和旧版本的Edge 中使用。

    TextEncoder 只能编码为 UTF-8,而TextDecoder 支持其他编码。我用它来解码日文文本(Shift-JIS):

    // Shift-JIS encoded text; must be a byte array due to values 129 and 130.
    var arr = [130, 108, 130, 102, 130, 80, 129,  64, 130, 102, 130,  96, 130, 108, 130, 100,
               129,  64, 130,  99, 130, 96, 130, 115, 130,  96, 129, 124, 130,  79, 130, 80];
    // Convert to byte array
    var data = new Uint8Array(arr);
    // Decode with TextDecoder
    var decoded = new TextDecoder("shift-jis").decode(data.buffer);
    console.log(decoded);
    

    【讨论】:

    • .decode( ) 不适用于字符串,因此如果您尝试解码恰好是 utf8 格式的字节字符串(在某些环境中可能发生),则没有用
    • 如果你有一个像“DEADBEEF”这样的十六进制字节串,你不能直接使用它。您需要将其转换为 TypedArray 才能对其进行解码。可以在 4 行代码中完成:paste2.org/5KHPxdVO
    • 在我的例子中,我实际上有一个 Javascript (UTF-16) 字符串,它具有 UTF-8 字符代码。实际上比这更糟糕,因为 0x80 再次被表示为其他东西(欧元符号的 unicode)等等。仍在尝试制定更好的解决方案,我应该能够将数据读入一个数组。但不幸的是,TextDecoder 是 IE/Edge 的问题。
    【解决方案2】:
    function convertByte()
    {
        var c=document.getElementById("str").value;
        var arr = [];
        var i=0;
        for(var ind=0;ind<c.length;ind++)
        {
            arr[ind]=c.charCodeAt(i);
            i++;
        }    
        document.getElementById("result").innerHTML="The converted value is "+arr.join("");    
    }
    

    【讨论】:

    • 欢迎来到 Stack Overflow。仅代码答案通常可以通过解释它们的工作方式和原因来改进,并且在使用现有答案和已接受答案为旧问题添加答案的情况下,指出此答案解决的问题的新方面。跨度>
    【解决方案3】:

    由于 JavaScript 中没有纯 byte 类型,我们可以将字节数组表示为数字数组,其中每个数字代表一个字节,因此将具有介于 0 和 255 之间的整数值。

    这是一个简单的函数,它可以将 JavaScript 字符串转换为包含字符串的 UTF-8 编码的数字数组:

    function toUtf8(str) {
        var value = [];
        var destIndex = 0;
        for (var index = 0; index < str.length; index++) {
            var code = str.charCodeAt(index);
            if (code <= 0x7F) {
                value[destIndex++] = code;
            } else if (code <= 0x7FF) {
                value[destIndex++] = ((code >> 6 ) & 0x1F) | 0xC0;
                value[destIndex++] = ((code >> 0 ) & 0x3F) | 0x80;
            } else if (code <= 0xFFFF) {
                value[destIndex++] = ((code >> 12) & 0x0F) | 0xE0;
                value[destIndex++] = ((code >> 6 ) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 0 ) & 0x3F) | 0x80;
            } else if (code <= 0x1FFFFF) {
                value[destIndex++] = ((code >> 18) & 0x07) | 0xF0;
                value[destIndex++] = ((code >> 12) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 6 ) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 0 ) & 0x3F) | 0x80;
            } else if (code <= 0x03FFFFFF) {
                value[destIndex++] = ((code >> 24) & 0x03) | 0xF0;
                value[destIndex++] = ((code >> 18) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 12) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 6 ) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 0 ) & 0x3F) | 0x80;
            } else if (code <= 0x7FFFFFFF) {
                value[destIndex++] = ((code >> 30) & 0x01) | 0xFC;
                value[destIndex++] = ((code >> 24) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 18) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 12) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 6 ) & 0x3F) | 0x80;
                value[destIndex++] = ((code >> 0 ) & 0x3F) | 0x80;
            } else {
                throw new Error("Unsupported Unicode character \"" 
                    + str.charAt(index) + "\" with code " + code + " (binary: " 
                    + toBinary(code) + ") at index " + index
                    + ". Cannot represent it as UTF-8 byte sequence.");
            }
        }
        return value;
    }
    
    function toBinary(byteValue) {
        if (byteValue < 0) {
            byteValue = byteValue & 0x00FF;
        }
        var str = byteValue.toString(2);
        var len = str.length;
        var prefix = "";
        for (var i = len; i < 8; i++) {
            prefix += "0";
        }
        return prefix + str;
    }
    

    【讨论】:

      【解决方案4】:

      我使用的是Joni's solution,它工作得很好,但是这个要短得多。

      这是受到Mozilla's Base64 Unicode discussion 解决方案#3 的 atobUTF16() 函数的启发

      function convertStringToUTF8ByteArray(str) {
          let binaryArray = new Uint8Array(str.length)
          Array.prototype.forEach.call(binaryArray, function (el, idx, arr) { arr[idx] = str.charCodeAt(idx) })
          return binaryArray
      }
      

      【讨论】:

        【解决方案5】:

        您可以使用 FileReader 原样保存字符串。

        将字符串保存在一个 blob 中并调用 readAsArrayBuffer()。然后 onload-event 会产生一个 arraybuffer,它可以转换为 Uint8Array。 不幸的是,这个调用是异步的。

        这个小功能可以帮到你:

        function stringToBytes(str)
        {
            let reader = new FileReader();
            let done = () => {};
        
            reader.onload = event =>
            {
                done(new Uint8Array(event.target.result), str);
            };
            reader.readAsArrayBuffer(new Blob([str], { type: "application/octet-stream" }));
        
            return { done: callback => { done = callback; } };
        }
        

        这样称呼它:

        stringToBytes("\u{1f4a9}").done(bytes =>
        {
            console.log(bytes);
        });
        

        输出: [240, 159, 146, 169]

        解释:

        JavaScript 使用 UTF-16 和代理对将 unicode 字符存储在内存中。要在原始二进制字节流中保存 unicode 字符,需要进行编码。 通常并且在大多数情况下,UTF-8 用于此目的。如果不使用编码,则无法保存 unicode 字符,只能保存 ASCII 到 0x7f。

        FileReader.readAsArrayBuffer() 使用 UTF-8。

        【讨论】:

          【解决方案6】:

          Google Closure 库具有与 UTF-8 和字节数组相互转换的函数。如果不想使用整个库,可以从here 复制函数。为了完整起见,将字符串转换为 UTF-8 字节数组的代码是:

          goog.crypt.stringToUtf8ByteArray = function(str) {
            // TODO(user): Use native implementations if/when available
            var out = [], p = 0;
            for (var i = 0; i < str.length; i++) {
              var c = str.charCodeAt(i);
              if (c < 128) {
                out[p++] = c;
              } else if (c < 2048) {
                out[p++] = (c >> 6) | 192;
                out[p++] = (c & 63) | 128;
              } else if (
                  ((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
                  ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
                // Surrogate Pair
                c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
                out[p++] = (c >> 18) | 240;
                out[p++] = ((c >> 12) & 63) | 128;
                out[p++] = ((c >> 6) & 63) | 128;
                out[p++] = (c & 63) | 128;
              } else {
                out[p++] = (c >> 12) | 224;
                out[p++] = ((c >> 6) & 63) | 128;
                out[p++] = (c & 63) | 128;
              }
            }
            return out;
          };
          

          【讨论】:

          • Google 将关闭移至 github。更新了链接(也更新了代码 sn-p,因为函数实现也发生了变化)。
          • 这里是更新的链接:stringToUtf8ByteArray()
          【解决方案7】:

          假设问题是关于一个 DOMString 作为输入,目标是获取一个数组,当它被解释为字符串(例如写入磁盘上的文件)时,将是 UTF-8 编码:

          现在几乎所有现代浏览器support Typed Arrays,如果不列出这种方法,那就太丢人了:

          • 根据W3C,支持文件 API 的软件应在其 Blob 构造函数中接受 DOMString(另请参阅:String encoding when constructing a Blob
          • 可以使用 文件阅读器.readAsArrayBuffer() 函数将 Blob 转换为 ArrayBuffer
          • 使用 DataView 或使用 File Reader 读取的缓冲区构造 Typed Array,可以访问 ArrayBuffer 的每个字节

          例子:

          // Create a Blob with an Euro-char (U+20AC)
          var b = new Blob(['€']);
          var fr = new FileReader();
          
          fr.onload = function() {
              ua = new Uint8Array(fr.result);
              // This will log "3|226|130|172"
              //                  E2  82  AC
              // In UTF-16, it would be only 2 bytes long
              console.log(
                  fr.result.byteLength + '|' + 
                  ua[0]  + '|' + 
                  ua[1] + '|' + 
                  ua[2] + ''
              );
          };
          fr.readAsArrayBuffer(b);
          

          JSFiddle 上玩。我还没有对此进行基准测试,但我可以想象这对于作为输入的大型 DOMString 是有效的。

          【讨论】:

          • 这很棒。无需在 JS 中进行疯狂的位旋转,只需将其直接传递给 Blob 构造函数即可。谢谢!
          【解决方案8】:

          UTF-8编码Unicode的逻辑基本是:

          • 每个字符最多可以使用 4 个字节。使用尽可能少的字节数。
          • 不超过 U+007F 的字符使用单个字节进行编码。
          • 对于多字节序列,第一个字节中前导 1 的位数给出了字符的字节数。第一个字节的其余位可用于对字符的位进行编码。
          • 连续字节以 10 开头,其他 6 位对字符的位进行编码。

          这是我不久前编写的一个函数,用于在 UTF-8 中编码 JavaScript UTF-16 字符串:

          function toUTF8Array(str) {
              var utf8 = [];
              for (var i=0; i < str.length; i++) {
                  var charcode = str.charCodeAt(i);
                  if (charcode < 0x80) utf8.push(charcode);
                  else if (charcode < 0x800) {
                      utf8.push(0xc0 | (charcode >> 6), 
                                0x80 | (charcode & 0x3f));
                  }
                  else if (charcode < 0xd800 || charcode >= 0xe000) {
                      utf8.push(0xe0 | (charcode >> 12), 
                                0x80 | ((charcode>>6) & 0x3f), 
                                0x80 | (charcode & 0x3f));
                  }
                  // surrogate pair
                  else {
                      i++;
                      // UTF-16 encodes 0x10000-0x10FFFF by
                      // subtracting 0x10000 and splitting the
                      // 20 bits of 0x0-0xFFFFF into two halves
                      charcode = 0x10000 + (((charcode & 0x3ff)<<10)
                                | (str.charCodeAt(i) & 0x3ff));
                      utf8.push(0xf0 | (charcode >>18), 
                                0x80 | ((charcode>>12) & 0x3f), 
                                0x80 | ((charcode>>6) & 0x3f), 
                                0x80 | (charcode & 0x3f));
                  }
              }
              return utf8;
          }
          

          【讨论】:

          【解决方案9】:

          JavaScript Strings 是 stored in UTF-16。要获得 UTF-8,您必须自己转换 String

          一种方法是将encodeURIComponent()unescape 混合输出为UTF-8 字节URL 编码的mentioned on ecmanaut

          var utf8 = unescape(encodeURIComponent(str));
          
          var arr = [];
          for (var i = 0; i < utf8.length; i++) {
              arr.push(utf8.charCodeAt(i));
          }
          

          【讨论】:

          • 谢谢,它有效。但我想了解一下,如何将此 unicode 编码为 utf8 字节码转换。你能给我链接一篇关于它的文章吗?我没有找到任何
          • @donkaka 我在我的帖子中链接到一个。 ecmanaut.blogspot.com/2006/07/…。您要手动逐个代码转换吗?
          • 是的。 encodeURIComponent 效果很好,但我想了解一下,utf8 字节码是如何生成的。
          • 维基百科其实对UTF-8转换有很好的总结。 en.wikipedia.org/wiki/UTF-8#Description 示例演示了原始代码点的位是如何扩展的,以及应用了哪些前缀来帮助以后解码。 UTF-16 surrogate pairs 编码变得复杂,但基于 bitwise shifting and masking with AND or OR
          • 这里有更多示例,如果您想在 UTF-8 文本和十六进制、二进制或 base64 之间进行转换:jsfiddle.net/47zwb41o
          猜你喜欢
          • 2012-07-17
          • 2011-01-18
          • 2017-05-18
          • 2013-09-12
          • 2018-05-01
          • 2012-02-01
          • 2013-05-08
          • 2015-08-13
          • 1970-01-01
          相关资源
          最近更新 更多