36191


以前、お世話になり、ありがとうございました。  
知識が不足してるにもかかわらず、再びの質問でお世話になります。  
以前、入力された文字列を変換するのに、以下のコードを教えていただきました。  
=> 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]


CC BY-SA 3.0
2017-07-08T07:04:10.997
36190
2017-07-08T13:06:27.763
2017-07-08T12:39:51.420
21092
24195
question
javascript 文字コード
サロゲートペアを持つ""を171581として表示させるには？


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]


CC BY-SA 3.0
2017-07-08T07:45:22.157
36191
2017-07-08T07:45:22.157
5750
36190
answer


単一の例では要件がはっきりしませんが、おそらく文字の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`はいずれも比較的新しい機能です。アプリケーションのターゲット環境がこれらをサポートしているかについて、互換性を十分確認してください。


CC BY-SA 3.0
2017-07-08T07:46:04.100
36192
2017-07-08T09:57:08.130
2017-07-08T09:57:08.130
36190
answer


一応ECMAScript 2015(ES6)の場合は、以下のようにも書けます。

IEを除くモダンブラウザであれば、動作するかと思いますが、ターゲットとされているブラウザで動くかどうか事前にご確認されたほうが良いかと思います。

[code]

    const str = "a0Ａてすbと";
    const result = [...str].map(c => c.codePointAt(0)).join();
    console.log(result);
[/code]

**参考URL:**  
JavaScript Stringでサロゲートペアを扱う - teppeis blog  
<http://teppeis.hatenablog.com/entry/2014/01/surrogate-pair-in-javascript>


CC BY-SA 3.0
2017-07-08T12:57:12.777
36198
2017-07-08T13:06:27.763
2017-07-08T13:06:27.763
3068
3068
36190
answer