サロゲートペアを持つ""を171581として表示させるには？


以前、お世話になり、ありがとうございました。  
知識が不足してるにもかかわらず、再びの質問でお世話になります。  
以前、入力された文字列を変換するのに、以下のコードを教えていただきました。  
=> 55399,56893になります。これを  
=> 171581と変換するには、どこを変えたらいいのでしょうか？それとも、JavaScriptでは不可能なのでしょうか。よろしくお願いいたします。

[code]

    function decodeString(){
      var src = document.getElementById('src').value;
      var dest = '';
      for(i=0; i < src.length; i++) {
        dest += src.charCodeAt(i) + ',';
      }
      document.getElementById('dest').value = dest;
    }
    
[/code]




UTF-16のサロゲートは1文字目の先頭6ビットが`110110`であるかどうかで判別できます。

符号は先頭を除いた下位10ビット×2に格納されているのですが、サロゲートが使用される場合は16ビットで表現できない場合ですので実際の値は`2^16`が減算される仕様になっています。  
ですので結局

[code]

    ((1文字目 & 0x3ff) << 10) + (2文字目 & 0x3ff) + 0x10000
    
[/code]

で元の符号の値を求めることが出来、繰り返し処理は以下のようになります。

[code]

    for(i = 0; i < src.length; i++) {
        var c = src.charCodeAt(i);
        if (i + 1 < src.length && (c & 0xfc00) == 0xd800) {
            var c2 = src.charCodeAt(++i);
            dest += ((c & 0x3ff) << 10) + (c2 & 0x3ff) + 0x10000 + ",";
        } else {
            dest += c + ",";
        }
    }
    
[/code]




単一の例では要件がはっきりしませんが、おそらく文字のUnicode符号位置を値として取得したいということと理解しました。この用途でいえばネイティブの実装を用いるならば[String.prototype.codePointAt](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)が利用できます。

[code]

    console.assert("".codePointAt(0) === 171581); // U+29E3D
    console.assert("".codePointAt(1) === 56893); // lower; 56893
    
[/code]

`String.prototype.codePointAt`のインデックスは従来通りUTF-16符号化に基づく文字数、すなわち、

[code]

    console.assert("".length === 2);
    
[/code]

と数えてしまう仕様ですから、単純なインクリメントで「各文字に」ついて処理をするのは不適切です。codePointAtを用いれは次のように書き直せます。

[code]

    const src = "a0Ａてすbと";
    const dest = [];
    let i = 0;
    while (i < src.length) {
        const codePoint = src.codePointAt(i);
        dest.push(codePoint);
        i += codePoint > 0xFFFF ? 2 : 1;
    }
    console.log(dest); // Array [ 97, 48, 65313, 12390, 171581, 12377, 98, 12392 ]
    
[/code]

これはサロゲートペアのときは「jsにおいて2文字分」、それ以外は通常通り「jsにおいて1文字分として」インクリメントして、それぞれの文字について符号位置を取得するものです。

ちなみに、Stringオブジェクトの既定イテレータである[String.prototype[@@iterator]()](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/String/@@iterator)は
**サロゲートペアをサポートします** 。

> The @@iterator method returns a new Iterator object that iterates over the
> code points of a String value, returning each code point as a String value.

こちらを用いれば先ほどのコードは次のように書けます。

[code]

    const src = "a0Ａてすbと";
    const dest = [];
    for (const char of src) {
        dest.push(char.codePointAt(0));
    }
    console.log(dest); // Array [ 97, 48, 65313, 12390, 171581, 12377, 98, 12392 ]
    
[/code]

ただし、`String.prototype.codePointAt`,`String.prototype[@@iterator]()`,`for...of
statement`はいずれも比較的新しい機能です。アプリケーションのターゲット環境がこれらをサポートしているかについて、互換性を十分確認してください。

