【问题标题】:Convert UTF-8 String with only 8 Bits per Character转换每个字符只有 8 位的 UTF-8 字符串
【发布时间】:2016-10-18 11:21:08
【问题描述】:

我有一个 JavaScript 字符串,其中包含 charCode 大于 255 的字符。

我希望能够将该字符串编码/解码为另一个字符串,其所有 charCode 小于或等于 255。

对字符没有限制(例如:可以是不可打印的)。

我想要一个尽可能快的解决方案,并且生成尽可能小的字符串。

它也必须适用于任何 UTF-8 字符。

我发现encodeURI 正是这样做的,但它似乎占用了很多空间。

encodeURI('ĉ') === "%C4%89" // 6 bytes...

有什么比encodeURI更好的吗?

【问题讨论】:

  • 除了没有大于255的charCode之外,您对编码还有什么要求吗?是否允许有引号、空格、不可打印字符、NUL字符?
  • 无其他要求。数据以二进制形式发送。
  • 快速和尽可能小在某种程度上是相互排斥的。您可以尝试对字符串进行 LZW 压缩。您要压缩的字符串有多大,为什么需要压缩它?例如。如果是 GET 请求,也许你可以使用 POST 请求,这样可以非常有效地传输字节。
  • 您可以将每个字符 charcode 转换为 base 255,然后用一个未使用的字符分隔它们。
  • @AndrewMorton 我正在使用将对象编码为二进制缓冲区的压缩库。该库假定对象中字符串的每个字符都适合 1 个字节。

标签: javascript string utf-8


【解决方案1】:

UTF-8 已经是 unicode 文本的编码,每个字符使用 8 位。您可以简单地通过网络发送 UTF-8 字符串。

通常,JavaScript 字符串由 UTF-16 字符组成。

对于此类字符串,您可以将每个 UTF-16 字符编码为两个 8 位字符,也可以使用动态长度编码,例如 UTF-8。

如果您有许多非 ASCII 字符,第一个可能会产生较小的结果。

// See http://monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
function encode_utf8(s) {
  return unescape(encodeURIComponent(s));
}

function decode_utf8(s) {
  return decodeURIComponent(escape(s));
}

function encode_fixed_length(s) {
  let length = s.length << 1,
      bytes = new Array(length);
  for (let i = 0; i < length; ++i) {
    let code = s.charCodeAt(i >> 1);
    bytes[i] = code >> 8;
    bytes[++i] = code & 0xFF;
  }
  return String.fromCharCode.apply(undefined, bytes);
}

function decode_fixed_length(s) {
  let length = s.length,
      chars = new Array(length >> 1);
  for (let i = 0; i < length; ++i) {
    chars[i >> 1] = (s.charCodeAt(i) << 8) + s.charCodeAt(++i);
  }
  return String.fromCharCode.apply(undefined, chars);
}

string_1 = "\u0000\u000F\u00FF";
string_2 = "\u00FF\u0FFF\uFFFF";

console.log(encode_fixed_length(string_1)); // "\x00\x00\x00\x0F\x00\xFF"
console.log(encode_fixed_length(string_2)); // "\x00\xFF\x0F\xFF\xFF\xFF"

console.log(encode_utf8(string_1));         // "\x00\x0F\xC3\xBF" 
console.log(encode_utf8(string_2));         // "\xC3\xBF\xE0\xBF\xBF\xEF\xBF\xBF"

性能对比:https://jsfiddle.net/r0d9pm25/1/

Firefox 47 中 500000 次迭代的结果:

  • 6159.91ms encode_fixed_length()
  • 7177.35ms encode_utf8()

【讨论】:

    【解决方案2】:

    您可以使用.charCodeAt(position) 获取字符的ASCII 值。您可以使用此将一个字符拆分为多个字符。

    首先,通过遍历字符串来获取每个字符的字符代码。创建一个临时的空字符串,当字符代码高于当前字符的 255 时,将其除以 255,并放入一个 ÿ(扩展 ASCII 表的第 256 个字符),然后一旦低于 255,使用 @987654323 @,将其转换为字符,并将其放在临时字符串的末尾,最后将字符替换为该字符串。

    function encode(string) {
        var result = [];
        for (var i = 0; i < string.length; i++) {
        var charCode = string.charCodeAt(i);
            var temp = "";
            while (charCode > 255) {
                temp += "ÿ";
                charCode -= 255;
            }
            result.push(temp + String.fromCharCode(charCode));
        }
        return result.join(",");
    }
    

    上面的编码器在每个组后面都放了一个逗号,这可能会导致解码出现问题,所以我们需要使用,(?!,) 正则表达式来匹配多个逗号中的最后一个逗号。

    function decode(string) {
        var characters = string.split(/,(?!,)/g);
        var result = "";
        for (var i = 0; i < characters.length; i++) {
            var charCode = 0;
            for (var j = 0; j < characters[i].length; j++) {
                charCode += characters[i].charCodeAt(j);
            }
            result += String.fromCharCode(charCode);
        }
        return result;
    }
    

    【讨论】:

    • 看起来不错,但这只是答案的一半。我也需要decode 函数。而且我相信尝试对"ÿ" 进行编码可能会导致问题。
    • 当前代码不起作用。试试decode(encode('ĉ')) == 'ĉ'。我会选择 RemcoGerlich 的答案,因为它也快得多。无论如何,谢谢。
    • @RainingChain 呵呵,我知道是什么问题了,让我解决一下
    • @RainingChain 现在可以了,我不知道你从哪里得到“其他更快”的部分
    • encodeURIComponentunescape 是本机代码,速度很快。您的解决方案使用了字符串连接和.split,它们很慢。
    【解决方案3】:

    您想要做的是将您的字符串编码为 UTF8。谷歌搜索如何在 Javascript 中做到这一点,我发现 http://monsur.hossa.in/2012/07/20/utf-8-in-javascript.html ,它给出:

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

    或简而言之,几乎与您已经找到的完全一样,加上将“%xx”代码转义为一个字节。

    【讨论】:

    • 嗯,我尝试了一个长字符串,它输出了一个字符,它不在扩展的 ASCII 表中
    • 什么是“扩展 ASCII 表”?
    • 扩展 ASCII 表是包含 0-255 而不是 0-127 字符代码的 ASCII 表(这是正常的 ASCII 表)
    • 没有包含 0-255 字符代码的 ascii 表。对于不同的语言等,有许多不同的此类表(虽然不称为 ASCII)。 ISO 8859(如 latin-1)、Windows 代码页等。只有一个 ASCII 表,即从 0 到 127 的那个。
    • @Bálint:那张桌子错了。您也可以链接到 cs.stanford.edu/people/miles/iso8859.htmlen.wikipedia.org/wiki/Windows-1252#Code_page_layout 或其他任何内容。我实际上不知道您链接到的页面是什么编码,但它不是“扩展 ASCII”。
    猜你喜欢
    • 2014-07-11
    • 2013-03-02
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多