【问题标题】:Decode UTF-8 with Javascript使用 Javascript 解码 UTF-8
【发布时间】:2012-11-01 15:08:42
【问题描述】:

我在 XHTML 网页中有 Javascript,它正在传递 UTF-8 编码的字符串。它需要继续通过 UTF-8 版本,并对其进行解码。如何解码 UTF-8 字符串进行显示?

<script type="text/javascript">
// <![CDATA[
function updateUser(usernameSent){
    var usernameReceived = usernameSent; // Current value: Größe
    var usernameDecoded = usernameReceived;  // Decode to: Größe
    var html2id = '';
    html2id += 'Encoded: ' + usernameReceived + '<br />Decoded: ' + usernameDecoded;
    document.getElementById('userId').innerHTML = html2id;
}
// ]]>
</script>

【问题讨论】:

  • 这不是你用 JavaScript 来解决的问题。解决它的方法是添加一个适当的meta 标记,如&lt;meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /&gt; 和XML 声明,如&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  • 什么? 只要你的网页是 UTF-8 编码的,js 就会把字符串当作 UTF-8 编码,encodeURIComponent()decodeURIComponent() 会假设数据为 UTF-8 编码。
  • "Größe" 不是 UTF-8(嗯,它可能是,但本质上不是),它是一个 混乱。它已经坏了。好几次,显然。它不需要“解码”,只要它出现故障和损坏都需要修复。提供更多的上下文信息,否则很难提供帮助。
  • 不要随意申请utf8_encode。你需要它吗?你知道你为什么需要它吗?
  • “用户尝试使用它”中的“它”是指UTF-8?那么你不需要utf8_encode。不必要。 utf8_encode 将字符串的编码从 ISO 8859-1 转换为 UTF-8。即使字符串已经是 UTF-8,它也会尝试这样做。 UTF-8“Größe”→utf8_encode→“GröÃe”→utf8_encode“GröÃÂe”。如果你在不需要它的时候使用它,你的琴弦就会搞砸。

标签: javascript unicode utf8-decode xhtml-transitional


【解决方案1】:

正如其他人建议的那样,最好使用Encoding API。但是如果你需要支持 IE(出于某种奇怪的原因) MDN 推荐这个 repo FastestSmallestTextEncoderDecoder

如果你需要使用 polyfill 库:

    import {encode, decode} from "fastestsmallesttextencoderdecoder";

然后(不考虑polyfill)进行编码和解码:

    // takes in USVString and returns a Uint8Array object
    const encoded = new TextEncoder().encode('€')
    console.log(encoded);

    // takes in an ArrayBuffer or an ArrayBufferView and returns a DOMString
    const decoded = new TextDecoder().decode(encoded);
    console.log(decoded);

【讨论】:

【解决方案2】:

也许使用textDecoder 就足够了。

在 IE 中不支持。

var decoder = new TextDecoder('utf-8'),
    decodedMessage;

decodedMessage = decoder.decode(message.data);

处理非 UTF8 文本

在这个例子中,我们解码俄语文本“Привет, мир!”,意思是“你好,世界”。在我们的 TextDecoder() 构造函数中,我们指定适用于西里尔文的 Windows-1251 字符编码。

    let win1251decoder = new TextDecoder('windows-1251');
    let bytes = new Uint8Array([207, 240, 232, 226, 229, 242, 44, 32, 236, 232, 240, 33]);
    console.log(win1251decoder.decode(bytes)); // Привет, мир!

TextDecoder 的接口描述为here

从字符串中检索字节数组同样简单:

const decoder = new TextDecoder();
const encoder = new TextEncoder();

const byteArray = encoder.encode('Größe');
// converted it to a byte array

// now we can decode it back to a string if desired
console.log(decoder.decode(byteArray));

如果您使用不同的编码,则必须在编码时对其进行补偿。 TextEncoder 的构造函数中的参数是here 列出的任何一种有效编码。

【讨论】:

  • 这只是在现有的混乱中增加了一层混乱。还有一个实验性
  • @ÁlvaroGonzález 但它可以工作并且可能是标准的(未来的浏览器也需要支持这个,好吗?)
  • 现在这不是实验性的,在所有现代浏览器中都有很好的支持,绝对是每个人的正确选择(除非你仍然必须支持 IE)
  • 从哪里获取 message.data?
  • @JamieHutber 也许你正在寻找这个?:developer.mozilla.org/en-US/docs/Web/API/TextDecoder
【解决方案3】:

您应该使用decodeURI

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI

就这么简单:

decodeURI('https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B');
// "https://developer.mozilla.org/ru/docs/JavaScript_шеллы"

考虑在try catch 块内使用它,以免丢失URIError

它还支持完整的浏览器。

【讨论】:

    【解决方案4】:

    这是一个包含大量错误报告的解决方案。

    它将采用 UTF-8 编码的字节数组(其中字节数组表示为 数字数组,每个数字是 0 到 255 之间的整数) 并将生成一个 Unicode 字符的 JavaScript 字符串。

    function getNextByte(value, startByteIndex, startBitsStr, 
                         additional, index) 
    {
        if (index >= value.length) {
            var startByte = value[startByteIndex];
            throw new Error("Invalid UTF-8 sequence. Byte " + startByteIndex 
                + " with value " + startByte + " (" + String.fromCharCode(startByte) 
                + "; binary: " + toBinary(startByte)
                + ") starts with " + startBitsStr + " in binary and thus requires " 
                + additional + " bytes after it, but we only have " 
                + (value.length - startByteIndex) + ".");
        }
        var byteValue = value[index];
        checkNextByteFormat(value, startByteIndex, startBitsStr, additional, index);
        return byteValue;
    }
    
    function checkNextByteFormat(value, startByteIndex, startBitsStr, 
                                 additional, index) 
    {
        if ((value[index] & 0xC0) != 0x80) {
            var startByte = value[startByteIndex];
            var wrongByte = value[index];
            throw new Error("Invalid UTF-8 byte sequence. Byte " + startByteIndex 
                 + " with value " + startByte + " (" +String.fromCharCode(startByte) 
                 + "; binary: " + toBinary(startByte) + ") starts with " 
                 + startBitsStr + " in binary and thus requires " + additional 
                 + " additional bytes, each of which shouls start with 10 in binary."
                 + " However byte " + (index - startByteIndex) 
                 + " after it with value " + wrongByte + " (" 
                 + String.fromCharCode(wrongByte) + "; binary: " + toBinary(wrongByte)
                 +") does not start with 10 in binary.");
        }
    }
    
    function fromUtf8 (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;
        }
    

    【讨论】:

      【解决方案5】:

      回答最初的问题:以下是在 javascript 中解码 utf-8 的方法:

      http://ecmanaut.blogspot.ca/2006/07/encoding-decoding-utf8-in-javascript.html

      具体来说,

      function encode_utf8(s) {
        return unescape(encodeURIComponent(s));
      }
      
      function decode_utf8(s) {
        return decodeURIComponent(escape(s));
      }
      

      我们在生产代码中使用它已有 6 年了,它运行良好。

      但是请注意,不推荐使用 escape() 和 unescape()。 See this.

      【讨论】:

      • 我尝试过使用decodeURIComponent(escape(usernameReceived))decodeURIComponent(usernameReceived),但都没有转换usernameReceived。你能展示一些功能代码吗?
      • 这是我的代码:s = decodeURIComponent(escape(s));请注意,您必须将其放在 try/catch 块中。
      • 如果回答了问题,请考虑将答案标记为已接受,或者如果您仍有问题,请告诉我。
      • 这对我有用。但如您所知,转义方法 id 已弃用。我们使用的是 TypeScript,默认情况下它不存在。那么逃跑的最佳选择是什么。在这种情况下,encodeURI 和 encodeURIComponent 无法替换 escape her,因为它们会产生不同的输出。
      • 当一个弃用的功能实际上有用时,防止它被删除的最好方法是继续使用它而不是避免使用它。浏览器供应商使用使用情况统计信息来确定何时删除某项功能。
      【解决方案6】:

      这是我在经过更具体的 Google 搜索后发现的,而不仅仅是 UTF-8 编码/解码。因此,对于那些正在寻找转换库以在编码之间进行转换的人来说,就可以了。

      https://github.com/inexorabletash/text-encoding

      var uint8array = new TextEncoder().encode(str);
      var str = new TextDecoder(encoding).decode(uint8array);
      

      从 repo 自述文件中粘贴

      支持编码规范中的所有编码:

      utf-8 ibm866 iso-8859-2 iso-8859-3 iso-8859-4 iso-8859-5 iso-8859-6 iso-8859-7 iso-8859-8 iso-8859-8-i iso -8859-10 iso-8859-13 iso-8859-14 iso-8859-15 iso-8859-16 koi8-r koi8-u macintosh windows-874 windows-1250 windows-1251 windows-1252 windows-1253 windows-1254 windows -1255 windows-1256 windows-1257 windows-1258 x-mac-cyrillic gb18030 hz-gb-2312 big5 euc-jp iso-2022-jp shift_jis euc-kr 替换 utf-16be utf-16le x-user-defined

      (其他名称可能支持某些编码,例如 ascii、iso-8859-1 等。有关每种编码的附加标签,请参阅编码。)

      【讨论】:

      【解决方案7】:

      使用我的 1.6KB library,你可以做到

      ToString(FromUTF8(Array.from(usernameReceived)))
      

      【讨论】:

        【解决方案8】:

        // 字符串转Utf8 ByteBuffer

        function strToUTF8(str){
          return Uint8Array.from(encodeURIComponent(str).replace(/%(..)/g,(m,v)=>{return String.fromCodePoint(parseInt(v,16))}), c=>c.codePointAt(0))
        }
        

        // Utf8 ByteArray 转字符串

        function UTF8toStr(ba){
          return decodeURIComponent(ba.reduce((p,c)=>{return p+'%'+c.toString(16),''}))
        }
        

        【讨论】:

        • 有人可以测试一下吗?另外,请详细记录参数和返回值,以帮助我们这些对 Unicode 感到困惑的人。谢谢。
        【解决方案9】:

        我认为最简单的方法是使用内置的 js 函数 decodeURI() / encodeURI()。

        function (usernameSent) {
          var usernameEncoded = usernameSent; // Current value: utf8
          var usernameDecoded = decodeURI(usernameReceived);  // Decoded
          // do stuff
        }
        

        【讨论】:

        • 听起来很简单。太容易了。你测试过这个吗?
        【解决方案10】:

        更新@Albert 的答案,为表情符号添加条件。

        function Utf8ArrayToStr(array) {
            var out, i, len, c;
            var char2, char3, char4;
        
            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;
             case 15:
                // 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
                char2 = array[i++];
                char3 = array[i++];
                char4 = array[i++];
                out += String.fromCodePoint(((c & 0x07) << 18) | ((char2 & 0x3F) << 12) | ((char3 & 0x3F) << 6) | (char4 & 0x3F));
        
                break;
            }
        
            return out;
        }
        

        【讨论】:

        • 注意:这适用于格式良好的 UTF-8 输入,但在某些情况下会在没有通知的情况下中断:例如,它假设剩余字节数正确,并且它们具有正确的继续序列0b10xxxxxx,而在 case 15 中,它应该只匹配 0b11110xxx,否则它可以解码非法代码点。
        【解决方案11】:

        这是一个处理所有 Unicode 代码点的解决方案,包括大写(4 字节)值,并受到所有现代浏览器(IE 和其他 > 5.5)的支持。它使用 decodeURIComponent(),但不使用已弃用的转义/取消转义函数:

        function utf8_to_str(a) {
            for(var i=0, s=''; i<a.length; i++) {
                var h = a[i].toString(16)
                if(h.length < 2) h = '0' + h
                s += '%' + h
            }
            return decodeURIComponent(s)
        }
        

        GitHub 上测试并可用

        从字符串创建 UTF-8:

        function utf8_from_str(s) {
            for(var i=0, enc = encodeURIComponent(s), a = []; i < enc.length;) {
                if(enc[i] === '%') {
                    a.push(parseInt(enc.substr(i+1, 2), 16))
                    i += 3
                } else {
                    a.push(enc.charCodeAt(i++))
                }
            }
            return a
        }
        

        GitHub 上测试并提供

        【讨论】:

        • 希望能详细说明参数和结果。 Unicode 让我非常困惑。
        【解决方案12】:

        @albert 的解决方案是我认为最接近的,但它最多只能解析 3 个字节的 utf-8 字符

        function utf8ArrayToStr(array) {
          var out, i, len, c;
          var char2, char3;
        
          out = "";
          len = array.length;
          i = 0;
        
          // XXX: Invalid bytes are ignored
          while(i < len) {
            c = array[i++];
            if (c >> 7 == 0) {
              // 0xxx xxxx
              out += String.fromCharCode(c);
              continue;
            }
        
            // Invalid starting byte
            if (c >> 6 == 0x02) {
              continue;
            }
        
            // #### MULTIBYTE ####
            // How many bytes left for thus character?
            var extraLength = null;
            if (c >> 5 == 0x06) {
              extraLength = 1;
            } else if (c >> 4 == 0x0e) {
              extraLength = 2;
            } else if (c >> 3 == 0x1e) {
              extraLength = 3;
            } else if (c >> 2 == 0x3e) {
              extraLength = 4;
            } else if (c >> 1 == 0x7e) {
              extraLength = 5;
            } else {
              continue;
            }
        
            // Do we have enough bytes in our data?
            if (i+extraLength > len) {
              var leftovers = array.slice(i-1);
        
              // If there is an invalid byte in the leftovers we might want to
              // continue from there.
              for (; i < len; i++) if (array[i] >> 6 != 0x02) break;
              if (i != len) continue;
        
              // All leftover bytes are valid.
              return {result: out, leftovers: leftovers};
            }
            // Remove the UTF-8 prefix from the char (res)
            var mask = (1 << (8 - extraLength - 1)) - 1,
                res = c & mask, nextChar, count;
        
            for (count = 0; count < extraLength; count++) {
              nextChar = array[i++];
        
              // Is the char valid multibyte part?
              if (nextChar >> 6 != 0x02) {break;};
              res = (res << 6) | (nextChar & 0x3f);
            }
        
            if (count != extraLength) {
              i--;
              continue;
            }
        
            if (res <= 0xffff) {
              out += String.fromCharCode(res);
              continue;
            }
        
            res -= 0x10000;
            var high = ((res >> 10) & 0x3ff) + 0xd800,
                low = (res & 0x3ff) + 0xdc00;
            out += String.fromCharCode(high, low);
          }
        
          return {result: out, leftovers: []};
        }
        

        这将返回{result: "parsed string", leftovers: [list of invalid bytes at the end]},以防您以块的形式解析字符串。

        编辑:修复了@unhammer 发现的问题。

        【讨论】:

        • 当我用 [195,165] 尝试这个时,我得到{"result":"","leftovers":[195, 165]} 而@Albert's 给出“å”
        • 你说得对,我在我的项目中修复了它,但在这篇文章中没有。对不起我的疏忽。
        • 没问题,现在似乎可以工作了 :-) 有趣的是,在有人测试它之前它已经得到了两个支持 :-) 现在 utf8ArrayToStr([240,159,154,133]) 给了我我的“?”
        【解决方案13】:

        我搜索了一个简单的解决方案,这对我很有效:

        //input data
        view = new Uint8Array(data);
        
        //output string
        serialString = ua2text(view);
        
        //convert UTF8 to string
        function ua2text(ua) {
            s = "";
            for (var i = 0; i < ua.length; i++) {
                s += String.fromCharCode(ua[i]);
            }
            return s;               
        }
        

        我唯一的问题是有时我一次只能得到一个角色。这可能是我的arraybuffer来源的设计。我正在使用https://github.com/xseignard/cordovarduino 读取安卓设备上的串行数据。

        【讨论】:

        • 这实际上并不解码 UTF-8。例如,C3 BC 应解码为ü,但您的答案返回ü
        【解决方案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;
        }
        

        查看JSFiddle demo

        另请参阅相关问题:herehere

        【讨论】:

        • 赞成真正理解解码 UTF-8 是什么。
        • 此代码不正确。 fromCharCode 接受 UTF-16 值,因此您需要在调用它之前转换为 UTF-16。
        猜你喜欢
        • 2021-03-22
        • 2018-09-11
        • 1970-01-01
        • 2015-08-10
        • 2012-11-07
        • 2014-01-24
        • 1970-01-01
        • 2013-04-21
        • 2023-03-28
        相关资源
        最近更新 更多