BMP之外的JavaScriptstring

BMP是基本的多语言平面

根据JavaScript:好的部分

JavaScript是在Unicode是16位字符集的时候构build的,所以JavaScript中的所有字符都是16位宽。

这使我相信JavaScript使用UCS-2(不是UTF-16!),只能处理高达U + FFFF的字符。

进一步调查证实了这一点:

> String.fromCharCode(0x20001); 

fromCharCode方法在返回Unicode字符时似乎只使用最低16位。 尝试获得U + 20001(CJK统一表意文字20001)而不是返回U + 0001。

问题:是否可以在JavaScript中处理BMP后的字符?


2011-07-31:从Unicode支持 12张幻灯片好,坏,和(主要)丑陋涉及这个相当好的问题:

取决于“支持”的含义。 你当然可以使用代理将非UCS-2字符放在JSstring中,如果可以,浏览器将显示它们。

但是,JSstring中的每个项目都是一个单独的UTF-16代码单元。 没有语言级的处理全字符的支持:所有的标准string成员( lengthsplitslice等)都处理代码单位而不是字符,所以会很高兴地拆分代理对或保持无效的代理序列。

如果你想要代理意识的方法,恐怕你将不得不开始写他们自己! 例如:

 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字符的string,则必须重新实现JavaScript的String方法。 这是因为JavaScript计数字符作为每个16位代码值。 BMP之外的符号需要两个代码值来表示。 因此,你会遇到一些情况,其中一些符号被计为两个字符,而另一些则只计为一个。

我已经重新实现了以下方法将每个unicode代码点视为单个字符:.length,.charCodeAt,.fromCharCode,.charAt,.indexOf,.lastIndexOf,.splice和.split。

你可以在jsfiddle上查看: http : //jsfiddle.net/Y89Du/

这是没有评论的代码。 我testing了它,但它可能仍然有错误。 欢迎评论。

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

是的你可以。 虽然根据ECMAScript标准,直接在源文档中支持非BMP字符是可选的,但现代浏览器允许您使用它们。 自然地,文档编码必须正确地声明,并且对于大多数实际的目的,您将需要使用UTF-8编码。 而且,你需要一个可以处理UTF-8的编辑器,并且你需要一些input方法。 请参阅我的完整Unicodeinput实用程序。

使用合适的工具和设置,你可以写var foo = '𠀁'

非BMP字符将在内部表示为代理对,因此每个非BMP字符在string长度中计为2。