javascript和字符串操作w/utf-16代理对

Ben*_*tFX 16 javascript string unicode twitter utf-16

正在开发一个推特应用程序,偶然发现了utf-8(16)的世界.似乎大多数javascript字符串函数对代理对都是盲目的.我必须重新编码一些东西才能让它具有广泛的字符意识.

我有这个函数来解析字符串到数组,同时保留代理对.然后我将重新编码几个函数来处理数组而不是字符串.

function sortSurrogates(str){
  var cp = [];                 // array to hold code points
  while(str.length){           // loop till we've done the whole string
    if(/[\uD800-\uDFFF]/.test(str.substr(0,1))){ // test the first character
                               // High surrogate found low surrogate follows
      cp.push(str.substr(0,2)); // push the two onto array
      str = str.substr(2);     // clip the two off the string
    }else{                     // else BMP code point
      cp.push(str.substr(0,1)); // push one onto array
      str = str.substr(1);     // clip one from string 
    }
  }                            // loop
  return cp;                   // return the array
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,有什么比我更缺的东西吗?我看到很多人重申javascript本身处理utf-16,但我的测试让我相信,这可能是数据格式,但功能还不知道.我错过了一些简单的事吗?

编辑:帮助说明问题:

var a = "0123456789"; // U+0030 - U+0039 2 bytes each
var b = ""; // U+1D7D8 - U+1D7E1 4 bytes each
alert(a.length); // javascript shows 10
alert(b.length); // javascript shows 20
Run Code Online (Sandbox Code Playgroud)

Twitter看到并计算这两个长度为10个字符.

tch*_*ist 22

Javascript内部使用UCS-2,而不是UTF-16.因此,在Javascript中处理Unicode非常困难,我不建议尝试这样做.

至于Twitter的作用,你似乎在说代码单元并不是疯狂地用代码点来计算.

除非你别无选择,否则你应该使用一种实际支持Unicode的编程语言,它具有代码点接口,而不是代码单元接口.正如你所发现的,Javascript还不够好.

它有UCS-2诅咒,甚至比UTF-16诅咒更糟糕,已经足够糟糕了.我在OSCON讲话中讨论了所有这些,Unicode支持枪战:好,坏,和(大多)丑陋.

由于它的可怕诅咒,你必须用Javascript中的UCS-2手工模拟UTF-16,这简直就是疯了.

Javascript也遭受各种其他可怕的Unicode问题.它不支持字形或标准化或校对,所有这些都是你真正需要的.它的正则表达式被打破了,有时候是因为诅咒,有时只是因为人们弄错了.例如,Javascript无法表达像这样的正则表达式[-].Javascript甚至不支持casefolding,因此你不能编写类似的模式/???????/i并使其正确匹配στιγμας.

您可以尝试使用XRegEXp插件,但不会以这种方式消除诅咒.只有改为支持Unicode的语言才能做到这一点,而且不是其中之一.

  • @tchrist,我同意.如果您想处理许多解释器,则不能依赖所有代表UTF-16的补充代码点.如果您只需要处理一个或几个解释器,您可以测试:`var div = document.createElement("DIV"); div.innerHTML ="&#0x10000;"; var isUtf16 = div.firstChild.nodeValue.charCodeAt(0)== 0xd800;` (4认同)
  • EcmaScript 5 说实现可以是 UTF-16 或 UCS-2。“符合本标准的实施应按照 Unicode 标准 3.0 版或更高版本和 ISO/IEC 10646-1 来解释字符,**UCS-2 或 UTF-16** 作为采用的编码形式,实施级别 3 .” 来自 [第 2 章第 2 段](http://es5.github.com/#x2) (2认同)
  • 在我的或多或少最新版本的 Chrome、Firefox、Edge 甚至 Internet Explorer 中,`/ΣΤשΜαΣ/i.test('στιγμας')` 返回“true”。当前 [ECMA-262 v9.0](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-ecmascript-language-types-string-type) 定义,该字符串必须使用 UTF-16。另外,当前的 Javascript 有 `String.fromCodePoint` 和 `String.prototype.codePointAt`,它们实际上可以与 BMP 之上的代码点一起使用。也许您可以更新您的答案并提及,现代 javascript 使用 UTF-16? (2认同)

Tim*_*own 8

我把Unicode字符串处理对象的起点拼凑在了一起.它创建了一个调用的函数UnicodeString()接受或者是JavaScript字符串或表示Unicode码位整数的数组,并提供lengthcodePoints属性和toString()slice()方法.添加正则表达式支持会非常复杂,但是(indexOf()split()没有正则表达式支持)之类的东西应该很容易实现.

var UnicodeString = (function() {
    function surrogatePairToCodePoint(charCode1, charCode2) {
        return ((charCode1 & 0x3FF) << 10) + (charCode2 & 0x3FF) + 0x10000;
    }

    function stringToCodePointArray(str) {
        var codePoints = [], i = 0, charCode;
        while (i < str.length) {
            charCode = str.charCodeAt(i);
            if ((charCode & 0xF800) == 0xD800) {
                codePoints.push(surrogatePairToCodePoint(charCode, str.charCodeAt(++i)));
            } else {
                codePoints.push(charCode);
            }
            ++i;
        }
        return codePoints;
    }

    function codePointArrayToString(codePoints) {
        var stringParts = [];
        for (var i = 0, len = codePoints.length, codePoint, offset, codePointCharCodes; i < len; ++i) {
            codePoint = codePoints[i];
            if (codePoint > 0xFFFF) {
                offset = codePoint - 0x10000;
                codePointCharCodes = [0xD800 + (offset >> 10), 0xDC00 + (offset & 0x3FF)];
            } else {
                codePointCharCodes = [codePoint];
            }
            stringParts.push(String.fromCharCode.apply(String, codePointCharCodes));
        }
        return stringParts.join("");
    }

    function UnicodeString(arg) {
        if (this instanceof UnicodeString) {
            this.codePoints = (typeof arg == "string") ? stringToCodePointArray(arg) : arg;
            this.length = this.codePoints.length;
        } else {
            return new UnicodeString(arg);
        }
    }

    UnicodeString.prototype = {
        slice: function(start, end) {
            return new UnicodeString(this.codePoints.slice(start, end));
        },

        toString: function() {
            return codePointArrayToString(this.codePoints);
        }
    };


    return UnicodeString;
})();

var ustr = UnicodeString("fbar");
document.getElementById("output").textContent = "String: '" + ustr + "', length: " + ustr.length + ", slice(2, 4): " + ustr.slice(2, 4);
Run Code Online (Sandbox Code Playgroud)
<div id="output"></div>
Run Code Online (Sandbox Code Playgroud)


sle*_*han 5

以下是一些在JavaScript中处理代理项对时可能有用的脚本:


rum*_*pel 5

Javascript 字符串迭代器可以为您提供实际字符而不是代理代码点:

>>> [..."0123456789"]
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
>>> [...""]
["", "", "", "", "", "", "", "", "", ""]
>>> [..."0123456789"].length
10
>>> [...""].length
10
Run Code Online (Sandbox Code Playgroud)