【问题标题】:Uint8Array to string in JavascriptUint8Array 到 Javascript 中的字符串
【发布时间】:2012-02-14 18:10:26
【问题描述】:

我有一些 UTF-8 编码的数据存在于 Javascript 中的一系列 Uint8Array 元素中。有没有一种有效的方法可以将这些解码为常规的 javascript 字符串(我相信 Javascript 使用 16 位 Unicode)?我不想同时添加一个字符,因为字符串连接会占用大量 CPU。

【问题讨论】:

  • 不确定它是否有效,但我在从 BrowserFS 读取文件时使用 u8array.toString(),当您调用 fs.readFile 时会暴露 Uint8Array 对象。
  • @jcubic 对我来说,toString on Uint8Array 返回逗号分隔的数字,例如 "91,50,48,49,57,45" (Chrome 79)

标签: javascript


【解决方案1】:

对于 ES6 和 UTF8 字符串

decodeURIComponent(escape(String.fromCharCode(...uint8arrData)))

【讨论】:

    【解决方案2】:

    使用香草,浏览器端,从麦克风录音,base64 功能对我有用(我必须实现一个音频发送功能到聊天)。

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

    现在是完整的代码。感谢 Bryan Jennings 和 breakspirit@py4u.net 提供代码。

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

    【讨论】:

      【解决方案3】:

      Uint8Array 转字符串

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

      Uint8Array 的字符串

      let uint8arr = new Uint8Array(Buffer.from(data,'base64')); 
      

      【讨论】:

        【解决方案4】:

        使用 base64 作为编码格式效果很好。这就是在 Firefox Send 中通过 url 传递秘密的实现方式。您将需要 base64-js 包。以下是 Send 源代码中的函数:

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

        【讨论】:

          【解决方案5】:

          到目前为止,对我来说最简单的方法是:

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

          【讨论】:

            【解决方案6】:

            Encoding standard 中的TextEncoderTextDecoder,由stringencoding library 填充,在字符串和ArrayBuffers 之间进行转换:

            var uint8array = new TextEncoder().encode("¢");
            var string = new TextDecoder().decode(uint8array);
            

            【讨论】:

            • 对于像我这样懒惰的人,npm install text-encodingvar textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;。不用了。
            • 当心 npm 文本编码库,webpack 包分析器显示该库很大
            • nodejs.org/api/string_decoder.html 来自示例: const { StringDecoder } = require('string_decoder');常量解码器 = 新 StringDecoder('utf8'); const cent = Buffer.from([0xC2, 0xA2]); console.log(decoder.write(cent));
            • 请注意,Node.js 在 v11 中添加了 TextEncoder/TextDecoder API,因此如果您只针对当前的 Node 版本,则无需安装任何额外的包。
            • 我认为现在最好的 polyfill 是 FastestSmallestTextEncoderDecoder,正如 MDN website 所推荐的那样。
            【解决方案7】:

            在 NodeJS 中,我们有可用的缓冲区,并且使用它们进行字符串转换非常容易。更好的是,将 Uint8Array 转换为 Buffer 很容易。试试这段代码,它在 Node 中对我有用,基本上任何涉及 Uint8Arrays 的转换:

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

            我们只是从 Uint8Array 中提取 ArrayBuffer,然后将其转换为适当的 NodeJS 缓冲区。然后我们将 Buffer 转换为字符串(如果需要,可以输入 hex 或 base64 编码)。

            如果我们想从字符串转换回 Uint8Array,那么我们会这样做:

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

            请注意,如果您在转换为字符串时声明了类似 base64 的编码,那么如果您使用 base64 或您使用的任何其他编码,则必须使用 Buffer.from(str, "base64")

            这在没有模块的浏览器中不起作用! NodeJS 缓冲区只是不存在于浏览器中,因此除非您向浏览器添加 Buffer 功能,否则此方法将不起作用。不过这实际上很容易做到,只需使用像 this 这样的模块,它既小又快!

            【讨论】:

              【解决方案8】:

              如果你不能使用 TextDecoder API 因为it is not supported on IE:

              1. 可以使用Mozilla Developer Network website推荐的FastestSmallestTextEncoderDecoder polyfill;
              2. 您也可以使用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);

              【讨论】:

                【解决方案9】:

                我很沮丧地看到人们没有展示如何双向使用或展示事情在非平凡的 UTF8 字符串上工作。我找到了一个post on codereview.stackexchange.com,它有一些运行良好的代码。我用它把古老的符文转换成字节,在字节上测试一些密码,然后把东西转换回字符串。工作代码在 github here。为了清楚起见,我重命名了这些方法:

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

                单元测试使用这个 UTF-8 字符串:

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

                请注意,字符串长度只有 117 个字符,但编码后的字节长度为 234。

                如果我取消注释 console.log 行,我可以看到解码的字符串与编码的字符串相同(字节通过 Shamir 的秘密共享算法传递!):

                【讨论】:

                • 如果chars 太大,String.fromCharCode.apply(null, chars) 会出错。
                • 是无处不在还是只是一些浏览器,是否有文档记录?
                • 例如这里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.
                • 谢谢。就我而言,我正在对小字符串进行加密,所以这不是问题。你有解决长字符串的方法吗? :-)
                • 解决方案是批量处理 64k 个字符。
                【解决方案10】:

                试试这些功能,

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

                来源:https://gist.github.com/tomfa/706d10fed78c497731ac,向 Tomfa 致敬

                【讨论】:

                  【解决方案11】:
                  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;
                  }
                  

                  `

                  【讨论】:

                  • 添加一些描述来回答。 @人族
                  【解决方案12】:

                  在节点“Buffer instances are also Uint8Array instances”中,所以buf.toString() 在这种情况下有效。

                  【讨论】:

                  • 非常适合我。如此简单!但实际上Uint8Array 有 toString() 方法。
                  • 简洁优雅,不知道Buffer也是Uint8Array。谢谢!
                  • @doom 在浏览器端,Uint8Array.toString() 不会编译 utf-8 字符串,它会列出数组中的数值。因此,如果您所拥有的是来自另一个来源的 Uint8Array,而该 Uint8Array 并非碰巧也是一个 Buffer,那么您将需要创建一个来发挥作用:Buffer.from(uint8array).toString('utf-8')
                  • 如何使用“Buffer.prototype.toString.call(uint8array, ‘utf8’)”来避免创建新的缓冲区实例。
                  • 这在 Chrome 中不起作用。 Buffer 是 nodejs only.
                  【解决方案13】:

                  只要不经常调用所提供的函数并且仅用于中等大小的数组,Albert 给出的解决方案就可以很好地工作,否则效率极低。这是一个增强的 vanilla JavaScript 解决方案,适用于 Node 和浏览器,具有以下优点:

                  • 适用于所有八位字节数组大小

                  • 不生成中间丢弃字符串

                  • 在现代 JS 引擎上支持 4 字节字符(否则用“?”代替)

                  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('');
                      };
                  })();
                  

                  【讨论】:

                  • 这里最好的解决方案,因为它也处理 4 字节字符(例如表情符号)谢谢!
                  • 这反过来又是什么?
                  【解决方案14】:

                  这应该可行:

                  // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
                  
                  /* utf.js - UTF-8 <=> UTF-16 convertion
                   *
                   * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
                   * 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;
                  }
                  

                  它比其他解决方案更干净,因为它不使用任何 hack 也不依赖于浏览器 JS 功能,例如也适用于其他 JS 环境。

                  查看JSFiddle demo

                  另请参阅相关问题:herehere

                  【讨论】:

                  • 这似乎有点慢。但我发现宇宙中唯一有效的sn-p。很好的发现+采用!
                  • 我不明白为什么这没有更多的赞成票。通过 UTF-8 约定来处理小型 sn-ps 似乎非常明智。正如其他人所指出的那样,Async Blob + Filereader 非常适合大文本。
                  • 问题是如何在没有字符串连接的情况下做到这一点
                  • 效果很好,除了它不处理 4+ 字节序列,例如fromUTF8Array([240,159,154,133]) 变成空的(而 fromUTF8Array([226,152,131])→"☃"
                  • 为什么要排除案例 8、9、10 和 11?有什么特别的原因吗?案例15也是可能的,对吧? 15(1111) 表示使用了 4 个字节,不是吗?
                  【解决方案15】:

                  我正在使用这个 Typescript sn-p:

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

                  如果您需要 JavaScript 版本,请删除类型注释。 希望这会有所帮助!

                  【讨论】:

                  • OP 要求一次不要添加一个字符。此外,他不想将其显示为列表的字符串表示,而只是显示为字符串。此外,这不会将字符转换为字符串,而是显示其数字。
                  【解决方案16】:

                  这是我使用的:

                  var str = String.fromCharCode.apply(null, uint8Arr);
                  

                  【讨论】:

                  • 来自doc,这似乎无法解码UTF8。
                  • 这会将RangeError 抛出更大的文本。 “超出最大调用堆栈大小”
                  • 如果您将大型 Uint8Arrays 转换为二进制字符串并遇到 RangeError,请参阅 stackoverflow.com/a/12713326/471341 中的 Uint8ToString 函数。
                  • 当我输入 300+k 个字符时,IE 11 会抛出 SCRIPT28: Out of stack space,或者 Chrome 39 会抛出 RangeError。Firefox 33 还可以。 100+k 三个都可以运行。
                  • 这不会从en.wikipedia.org/wiki/UTF-8 上的示例 unicode 字符产生正确的结果。例如String.fromCharCode.apply(null, new Uint8Array([0xc2, 0xa2])) 不产生¢。
                  【解决方案17】:

                  在 Chrome 示例应用程序之一中找到,尽管这适用于较大的数据块,您可以在其中进行异步转换。

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

                  【讨论】:

                  • 正如你所说,除非要转换的缓冲区真的很大,否则这将执行得非常糟糕。在 V8 中实现的简单字符串(比如 10-40 字节)的同步 UTF-8 到 wchar 转换应该远小于一微秒,而我猜你的代码需要几百倍。还是谢谢你。
                  【解决方案18】:

                  按照@Sudhir 所说的去做,然后从逗号分隔的数字列表中获取一个字符串:

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

                  这会给你你想要的字符串, 如果它仍然相关

                  【讨论】:

                  • 抱歉,没有注意到您说您不想一次添加一个字符的最后一句话。希望这可以帮助其他没有 CPU 使用问题的人。
                  • 这不做 UTF8 解码。
                  • 更短:String.fromCharCode.apply(null, unitArr);。如前所述,它不处理 UTF8 编码,但有时如果您只需要 ASCII 支持但无法访问 TextEncoder/TextDecoder,这很简单。
                  • 答案提到了@Sudhir,但我搜索了页面,现在找到了这样的答案。所以最好内联他所说的任何内容
                  • 这将在较长的字符串上表现不佳。不要在字符串上使用 + 运算符。
                  猜你喜欢
                  • 2015-12-24
                  • 1970-01-01
                  • 2020-12-02
                  • 1970-01-01
                  • 2020-03-17
                  • 2017-09-15
                  • 2020-04-12
                  • 1970-01-01
                  • 2019-01-22
                  相关资源
                  最近更新 更多