BMP之外的JavaScript字符串

BMP是基本的多语言平面 根据JavaScript:好的部分:   JavaScript是在16位字符集的时候构建的,因此JavaScript中的所有字符都是16位宽。 这让我相信JavaScript使用UCS-2(不是UTF-16!)并且只能处理U + FFFF以前的字符。 进一步调查证实了这一点:
> String.fromCharCode(0x20001);
返回Unicode字符时,
fromCharCode
方法似乎只使用最低16位。试图获得U + 20001(CJK统一表意文字20001)而不是返回U + 0001。 问题:是否可以在JavaScript中处理后BMP字符? 2011-07-31:来自Unicode支持枪战的第12个幻灯片:The Good,The Bad,& (大多数)Ugly很好地涵盖了与此相关的问题:     
已邀请:
取决于“支持”的含义。您当然可以使用代理项将非UCS-2字符放入JS字符串中,如果可以,浏览器将显示它们。 但是,JS字符串中的每个项目都是一个单独的UTF-16代码单元。处理完整字符没有语言级支持:所有标准字符串成员(
length
split
slice
等)都处理代码单元而不是字符,因此很乐意拆分代理对或保留无效的代理序列。 如果你想要代理意识的方法,我担心你必须自己开始写它们!例如:
String.prototype.getCodePointLength= function() {
    return this.length-this.split(/[uD800-uDBFF][uDC00-uDFFF]/g).length+1;
};

String.fromCodePoint= function() {
    var chars= Array.prototype.slice.call(arguments);
    for (var i= chars.length; i-->0;) {
        var n = chars[i]-0x10000;
        if (n>=0)
            chars.splice(i, 1, 0xD800+(n>>10), 0xDC00+(n&0x3FF));
    }
    return String.fromCharCode.apply(null, chars);
};
    
我得出了与bobince相同的结论。如果您想使用包含BMP之外的unicode字符的字符串,则必须重新实现javascript的String方法。这是因为javascript将字符计为每个16位代码值。 BMP之外的符号需要表示两个代码值。因此,您遇到一些情况,其中一些符号计为两个字符,有些符号只计为一个。 我重新实现了以下方法,将每个unicode代码点视为单个字符:。length,.charCodeAt,。fromCharCode,.charAt,.indexOf,.lastIndexOf,.splice和.split。 你可以在jsfiddle上查看:http://jsfiddle.net/Y89Du/ 这是没有评论的代码。我测试了它,但它可能仍然有错误。欢迎评论。
if (!String.prototype.ucLength) {
    String.prototype.ucLength = function() {
        // this solution was taken from 
        // http://stackoverflow.com/questions/3744721/javascript-strings-outside-of-the-bmp
        return this.length - this.split(/[uD800-uDBFF][uDC00-uDFFF]/g).length + 1;
    };
}

if (!String.prototype.codePointAt) {
    String.prototype.codePointAt = function (ucPos) {
        if (isNaN(ucPos)){
            ucPos = 0;
        }
        var str = String(this);
        var codePoint = null;
        var pairFound = false;
        var ucIndex = -1;
        var i = 0;  
        while (i < str.length){
            ucIndex += 1;
            var code = str.charCodeAt(i);
            var next = str.charCodeAt(i + 1);
            pairFound = (0xD800 <= code && code <= 0xDBFF && 0xDC00 <= next && next <= 0xDFFF);
            if (ucIndex == ucPos){
                codePoint = pairFound ? ((code - 0xD800) * 0x400) + (next - 0xDC00) + 0x10000 : code;
                break;
            } else{
                i += pairFound ? 2 : 1;
            }
        }
        return codePoint;
    };
}

if (!String.fromCodePoint) {
    String.fromCodePoint = function () {
        var strChars = [], codePoint, offset, codeValues, i;
        for (i = 0; i < arguments.length; ++i) {
            codePoint = arguments[i];
            offset = codePoint - 0x10000;
            if (codePoint > 0xFFFF){
                codeValues = [0xD800 + (offset >> 10), 0xDC00 + (offset & 0x3FF)];
            } else{
                codeValues = [codePoint];
            }
            strChars.push(String.fromCharCode.apply(null, codeValues));
        }
        return strChars.join("");
    };
}

if (!String.prototype.ucCharAt) {
    String.prototype.ucCharAt = function (ucIndex) {
        var str = String(this);
        var codePoint = str.codePointAt(ucIndex);
        var ucChar = String.fromCodePoint(codePoint);
        return ucChar;
    };
}

if (!String.prototype.ucIndexOf) {
    String.prototype.ucIndexOf = function (searchStr, ucStart) {
        if (isNaN(ucStart)){
            ucStart = 0;
        }
        if (ucStart < 0){
            ucStart = 0;
        }
        var str = String(this);
        var strUCLength = str.ucLength();
        searchStr = String(searchStr);
        var ucSearchLength = searchStr.ucLength();
        var i = ucStart;
        while (i < strUCLength){
            var ucSlice = str.ucSlice(i,i+ucSearchLength);
            if (ucSlice == searchStr){
                return i;
            }
            i++;
        }
        return -1;
    };
}

if (!String.prototype.ucLastIndexOf) {
    String.prototype.ucLastIndexOf = function (searchStr, ucStart) {
        var str = String(this);
        var strUCLength = str.ucLength();
        if (isNaN(ucStart)){
            ucStart = strUCLength - 1;
        }
        if (ucStart >= strUCLength){
            ucStart = strUCLength - 1;
        }
        searchStr = String(searchStr);
        var ucSearchLength = searchStr.ucLength();
        var i = ucStart;
        while (i >= 0){
            var ucSlice = str.ucSlice(i,i+ucSearchLength);
            if (ucSlice == searchStr){
                return i;
            }
            i--;
        }
        return -1;
    };
}

if (!String.prototype.ucSlice) {
    String.prototype.ucSlice = function (ucStart, ucStop) {
        var str = String(this);
        var strUCLength = str.ucLength();
        if (isNaN(ucStart)){
            ucStart = 0;
        }
        if (ucStart < 0){
            ucStart = strUCLength + ucStart;
            if (ucStart < 0){ ucStart = 0;}
        }
        if (typeof(ucStop) == 'undefined'){
            ucStop = strUCLength - 1;
        }
        if (ucStop < 0){
            ucStop = strUCLength + ucStop;
            if (ucStop < 0){ ucStop = 0;}
        }
        var ucChars = [];
        var i = ucStart;
        while (i < ucStop){
            ucChars.push(str.ucCharAt(i));
            i++;
        }
        return ucChars.join("");
    };
}

if (!String.prototype.ucSplit) {
    String.prototype.ucSplit = function (delimeter, limit) {
        var str = String(this);
        var strUCLength = str.ucLength();
        var ucChars = [];
        if (delimeter == ''){
            for (var i = 0; i < strUCLength; i++){
                ucChars.push(str.ucCharAt(i));
            }
            ucChars = ucChars.slice(0, 0 + limit);
        } else{
            ucChars = str.split(delimeter, limit);
        }
        return ucChars;
    };
}
    
最新的JavaScript引擎有
String.fromCodePoint
const ideograph = String.fromCodePoint( 0x20001 ); // outside the BMP
也是一个代码点迭代器,它可以获得代码点长度。
function countCodePoints( str )
{
    const i = str[Symbol.iterator]();
    let count = 0;
    while( !i.next().done ) ++count;
    return count;
}

console.log( ideograph.length ); // gives '2'
console.log( countCodePoints(ideograph) ); // '1'
    
是的你可以。虽然根据ECMAScript标准,直接在源文档中支持非BMP字符是可选的,但现代浏览器允许您使用它们。当然,必须正确声明文档编码,并且对于大多数实际目的,您需要使用UTF-8编码。而且,你需要一个可以处理UTF-8的编辑器,你需要一些输入法;见例如我的完整Unicode输入实用程序。 使用合适的工具和设置,您可以编写var foo ='

要回复问题请先登录注册