40

BMP はBasic Multilingual Plane です

JavaScriptによると: 良い部分:

JavaScript は、Unicode が 16 ビットの文字セットであったときに構築されたため、JavaScript のすべての文字は 16 ビット幅です。

このことから、JavaScript は (UTF-16 ではなく) UCS-2 を使用し、U+FFFF までの文字しか処理できないと思われます。

さらに調査すると、次のことが確認されます。

> String.fromCharCode(0x20001);

このfromCharCodeメソッドは、Unicode 文字を返すときに下位 16 ビットのみを使用しているようです。U+20001 (CJK 統一表意文字 20001) を取得しようとすると、代わりに U+0001 が返されます。

質問: JavaScript でポスト BMP 文字を処理することはまったく可能ですか?


2011-07-31: Unicode Support Shootout のスライド 12: The Good, The Bad, & the (mostly) Uglyは、これに関連する問題を非常によくカバーしています:

4

5 に答える 5

36

「サポート」が何を意味するかによります。サロゲートを使用して、UCS-2 以外の文字を JS 文字列に入れることができます。可能な場合、ブラウザーはそれらを表示します。

ただし、JS 文字列の各項目は個別の UTF-16 コード単位です。完全な文字を処理するための言語レベルのサポートはありません。すべての標準 String メンバー ( length、など) はすべてsplitslice文字ではなくコード単位を処理するため、サロゲート ペアを分割したり、無効なサロゲート シーケンスを保持したりできます。

サロゲートを意識したメソッドが必要な場合は、自分で書き始める必要があります。例えば:

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);
};
于 2010-09-21T10:16:11.347 に答える
2

ボビンスと同じ結論に達しました。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;
    };
}
于 2013-02-01T07:19:35.427 に答える
1

最近の JavaScript エンジンには.String.fromCodePoint

const ideograph = String.fromCodePoint( 0x20001 ); // outside the BMP

また、コードポイントの長さを取得するコードポイント iterator 。

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'
于 2017-12-24T19:46:41.210 に答える
0

はい、できます。ECMAScript 標準によると、ソース ドキュメントで非 BMP 文字を直接サポートすることはオプションですが、最新のブラウザーではそれらを使用できます。当然、ドキュメントのエンコーディングは適切に宣言する必要があり、ほとんどの場合、UTF-8 エンコーディングを使用する必要があります。さらに、UTF-8 を処理できるエディターと、いくつかの入力メソッドが必要です。たとえば、私の完全な Unicode 入力ユーティリティを参照してください。

適切なツールと設定を使用して、書くことができますvar foo = ''

非 BMP 文字は内部的にサロゲート ペアとして表されるため、各非 BMP 文字は文字列の長さで 2 としてカウントされます。

于 2012-12-10T07:26:00.003 に答える