BMP之外的JavaScript字符串
BMP正在 根据JavaScript:好的部分: JavaScript是在Unicode是16位字符集的时候构建的,所以JavaScript中的所有字符都是16位宽的 这让我相信JavaScript使用UCS-2(而不是UTF-16!),并且只能处理U+FFFF以下的字符 进一步调查证实:BMP之外的JavaScript字符串,javascript,unicode,utf-16,surrogate-pairs,astral-plane,Javascript,Unicode,Utf 16,Surrogate Pairs,Astral Plane,BMP正在 根据JavaScript:好的部分: JavaScript是在Unicode是16位字符集的时候构建的,所以JavaScript中的所有字符都是16位宽的 这让我相信JavaScript使用UCS-2(而不是UTF-16!),并且只能处理U+FFFF以下的字符 进一步调查证实: > String.fromCharCode(0x20001); fromCharCode方法在返回Unicode字符时似乎只使用最低的16位。尝试获取U+20001(CJK统一表意文字20001)将返
> String.fromCharCode(0x20001);
fromCharCode
方法在返回Unicode字符时似乎只使用最低的16位。尝试获取U+20001(CJK统一表意文字20001)将返回U+0001
问题:是否可以在JavaScript中处理post BMP字符
2011年7月31日:Unicode支持决战:好的、坏的和(大部分)丑陋的第十二张幻灯片非常好地涵盖了与此相关的问题:
取决于你所说的“支持”是什么意思。您当然可以使用代理将非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);
};
是的,你可以。尽管根据ECMAScript标准,直接在源文档中支持非BMP字符是可选的,但现代浏览器允许您使用它们。当然,文档编码必须正确声明,并且出于最实际的目的,您需要使用UTF-8编码。此外,您需要一个能够处理UTF-8的编辑器,并且需要一些输入法;例如,请参阅我的实用程序
使用合适的工具和设置,您可以编写
var foo='I得出与bobince相同的结论。如果要处理BMP之外包含unicode字符的字符串,必须重新实现javascript的字符串方法。这是因为javascript将字符计数为每个16位代码值。BMP之外的符号需要表示两个代码值。因此,您会遇到这样的情况:一些符号计为两个字符,而一些符号仅计为一个字符
我重新实现了以下方法,将每个unicode代码点视为单个字符:.length、.charCodeAt、.fromCharCode、.charAt、.indexOf、.lastIndexOf、.splice和.split
您可以在JSFIDLE上查看它:
这是没有注释的代码。我测试了它,但它可能仍然有错误。欢迎评论
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;
};
}
if(!String.prototype.nulength){
String.prototype.ucLength=函数(){
//这个解决方案是从
// http://stackoverflow.com/questions/3744721/javascript-strings-outside-of-the-bmp
返回this.length-this.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
};
}
if(!String.prototype.codePointAt){
String.prototype.codePointAt=函数(ucPos){
如果(伊斯南(ucPos)){
ucPos=0;
}
var str=String(this);
var-codePoint=null;
var pairFound=false;
var-ucIndex=-1;
var i=0;
而(i=0){
var ucSlice=str.ucSlice(i,i+ucSearchLength);
if(ucSlice==searchStr){
返回i;
}
我--;
}
返回-1;
};
}
if(!String.prototype.ucSlice){
String.prototype.ucSlice=函数(ucStart、ucStop){
var str=String(this);
var strUCLength=str.nucleLength();
if(isNaN(ucStart)){
ucStart=0;
}
如果(ucStart<0){
ucStart=StrucleLength+ucStart;
如果(ucStart<0){ucStart=0;}
}
if(typeof(ucStop)==“未定义”){
ucStop=结构长度-1;
}
如果(ucStop<0){
ucStop=结构长度+ucStop;
如果(ucStop<0){ucStop=0;}
}
var-ucChars=[];
var i=ucStart;
而(i
最近的JavaScript引擎有字符串。
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'
使用for(c of this)
指令,可以对包含非BMP字符的字符串进行各种计算。例如,要计算字符串长度,并获取字符串的第n个字符:
String.prototype.magicLength = function()
{
var c, k;
k = 0;
for (c of this) // iterate each char of this
{
k++;
}
return k;
}
String.prototype.magicCharAt = function(n)
{
var c, k;
k = 0;
for (c of this) // iterate each char of this
{
if (k == n) return c + "";
k++;
}
return "";
}
如果它使用UTF-16,那么你会期望基本多语言平面之外的字符能够使用代理项对得到支持。为什么你会期望它接受32位字符?非常感谢,我从来没有这样想过。@MichaelAaronSafyan:因为JavaScript没有类似于“char”的东西键入并String.fromCharCode()
返回一个字符串,期望它返回一个包含