BMPは 基本的な多言語面
JavaScriptによると:良い部分:
JavaScriptはUnicodeが16ビットの文字セットであったときに構築されたため、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をスライド:良い、悪い、そして(ほとんど)醜いこれに関連する問題を非常にうまくカバーしています:
「サポート」の意味によって異なります。確かに、サロゲートを使用してJS文字列にUCS-2以外の文字を入れることができ、可能であればブラウザはそれらを表示します。
ただし、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以外のシンボルは、2つのコード値を表す必要があります。したがって、一部のシンボルは2文字としてカウントされ、一部は1文字としてカウントされる場合があります。
各ユニコードコードポイントを単一の文字として扱うために、次のメソッドを再実装しました:.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を処理できるエディターが必要であり、いくつかの入力メソッドが必要です。たとえばを参照してください。 my フルUnicode入力 ユーティリティ。
適切なツールと設定を使用して、var foo = '????'
を記述できます。
非BMP文字は内部的にサロゲートペアとして表されるため、各非BMP文字は文字列の長さで2としてカウントされます。
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 "";
}