-->

2010-04-22

javascript の String.fromCharCode

String.fromCharCode(0x2a6b2) が失敗します。

𪚲 (𪚲)

理屈は 65536 以上(17ビット以上。0,1が17文字以上)の場合、
unicode番号の最大値から 65536 を引くと最大で 20ビット(1が20個。9999のような桁が上がる直前)になるので、
65536 以上の数値を 65536 引いて 10ビットずつの2つに分けて、
0~65535 の内、使われていない範囲を指定する6ビットを、分けた2つのそれぞれの頭につける。
(1文字目の 6ビット) (1文字目の 10ビット) (2文字目の 6ビット) (2文字目の 10ビット)
String.fromCharCode の場合、それを String.fromCharCode(1文字目の16ビット, 2文字目の16ビット) として渡すと1文字を表現できます。
1文字目の10ビット、2文字目の10ビットが変化するので
2^10 * 2^10 = 1,048,576
ということで100万種類ぐらいになると思います。

0 ~ 65535 までは16ビット
0xFFFF (bin)1111111111111111
0xFFFF (dec)65535
0xFFFF (bin, length)16
65536 以上は17ビット以上
0x10000 (bin)10000000000000000
0x10000 (dec)65536
0x10000 (bin, length)17
unicode の文字数
0x10FFFF (bin)100001111111111111111
0x10FFFF (dec)1114111
0x10FFFF (bin, length)21
65536 を引くと20ビット
0x10FFFF - 0x10000 (bin)11111111111111111111
0x10FFFF - 0x10000 (dec)1048575
0x10FFFF - 0x10000 (bin, length)20
num = 0x2A6B2 の場合
String.fromCharCode(0x2a6b2)文字化け
num (bin)101010011010110010
num (dec)173746
num (bin, length)18
# num (hex)2a6b2
num - 65536 した場合
num - 0x10000 (bin)11010011010110010
num - 0x10000 (dec)108210
num - 0x10000 (bin, length)17
# num - 0x10000 high,low1101001,1010110010
サロゲートペア1文字目に移動させる値
0xD800 (bin)1101100000000000
0xD800 (dec)55296
0xD800 (bin, length)16
# 0xD800 high,low110110,0000000000
# 110110 00 00000000d800
# 110110 01 00000000d900
# 110110 10 00000000da00
# 110110 11 00000000db00
# 110110 11 11111111dbff
サロゲートペア2文字目に移動させる値
0xDC00 (bin)1101110000000000
0xDC00 (dec)56320
0xDC00 (bin, length)16
# 0xDC00 high,low110111,0000000000
# 110111 00 00000000dc00
# 110111 01 00000000dd00
# 110111 10 00000000de00
# 110111 11 00000000df00
# 110111 11 11111111dfff
サロゲートペア1文字目
high ((num - 0x10000) >> 10)1101001
high mask1101100000000000
high result1101100001101001
high result (dec)55401
サロゲートペア2文字目
parseInt("1111111111", 2).toString(16)3ff
low ((num - 0x10000) & 0x3FF)1010110010
low mask1101110000000000
low result1101111010110010
low result (dec)57010
String.fromCharCode に2つ指定
String.fromCharCode(55401, 57010)𪚲

この文字設定をサロゲートペアといいます。
http://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_D000-DFFF
http://ja.wikipedia.org/wiki/Unicode#.E3.82.B5.E3.83.AD.E3.82.B2.E3.83.BC.E3.83.88.E3.83.9A.E3.82.A2

2進数の 1xxxxxxxxxx >> 10 は 1になります。
// 例. 一桁目が消えて2桁目が1桁目になります。(2で割る。右シフト)
alert(parseInt("010", 2) >> 1); // 1 (01)
alert(parseInt("100", 2) >> 1); // 2 (10)
alert(parseInt("110", 2) >> 1); // 3 (11)
alert(parseInt("011", 2) >> 1); // 1 (01)
alert(parseInt("101", 2) >> 1); // 2 (10)
alert(parseInt("111", 2) >> 1); // 3 (11)

2進数の ??????????xxxxxxxxxx & 0x3FF(2進数で1が10個) は xxxxxxxxxx になります。
// 例. それぞれの桁で両方とも1の場合に、それぞれの桁が1になります。
alert(parseInt("010", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("100", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("110", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("011", 2) & parseInt("001", 2)); // 1 (001)
alert(parseInt("101", 2) & parseInt("001", 2)); // 1 (001)
alert(parseInt("111", 2) & parseInt("001", 2)); // 1 (001)

high result, low result はオア演算です。
// 例. それぞれの桁でどちらかに1があれば、それぞれの桁を1にします。
alert(parseInt("010", 2) | parseInt("001", 2)); // 3 (011)
alert(parseInt("100", 2) | parseInt("001", 2)); // 5 (101)
alert(parseInt("110", 2) | parseInt("001", 2)); // 7 (111)
alert(parseInt("011", 2) | parseInt("001", 2)); // 3 (011)
alert(parseInt("101", 2) | parseInt("001", 2)); // 5 (101)
alert(parseInt("111", 2) | parseInt("001", 2)); // 7 (111)

ビット演算子は、他の言語でも同じように記述できる場合が多いです。
http://php.net/manual/ja/language.operators.bitwise.php

2進数、16進数は、理屈がわかっていれば暗算はできなくていいと思います。
buf = "";

buf += "### 0\n";
buf += parseInt("0",  2) + "\n"; // 0
buf += parseInt("0", 10) + "\n"; // 0
buf += parseInt("0", 16) + "\n"; // 0

buf += "### 1\n";
buf += parseInt("1",  2) + "\n"; // 1
buf += parseInt("1", 10) + "\n"; // 1
buf += parseInt("1", 16) + "\n"; // 1

//  2進数の10 = 0 1                             10
// 10進数の10 = 0 1 2 3 4 5 6 7 8 9             10
// 16進数の10 = 0 1 2 3 4 5 6 7 8 9 A B C D E F 10
buf += "### それぞれの 10\n";
buf += parseInt("10",  2) + "\n"; //  2 = ( 2 * 1)
buf += parseInt("10", 10) + "\n"; // 10 = (10 * 1)
buf += parseInt("10", 16) + "\n"; // 16 = (16 * 1)
buf += "### それぞれの 11\n";
buf += parseInt("11",  2) + "\n"; //  3 = ( 2 * 1) + 1
buf += parseInt("11", 10) + "\n"; // 11 = (10 * 1) + 1
buf += parseInt("11", 16) + "\n"; // 17 = (16 * 1) + 1

buf += "### 10進数と16進数の 99\n";
buf += parseInt(" 99", 10) + "\n"; //   99 =                 (10 * 9) + 9
buf += parseInt(" 99", 16) + "\n"; //  153 =                 (16 * 9) + 9
buf += "### 10進数と16進数の 999\n";
buf += parseInt("999", 10) + "\n"; //  999 = (10 * 10 * 9) + (10 * 9) + 9
buf += parseInt("999", 16) + "\n"; // 2457 = (16 * 16 * 9) + (16 * 9) + 9

buf += "### 16進数の F, FF\n";
buf += parseInt(" F", 16) + "\n";  //   15 =             15
buf += parseInt("FF", 16) + "\n";  //  255 = (16 * 15) + 15

buf += "### 2進数の 1111, 11111111\n";
buf += parseInt("    1111", 2) + "\n"; //   15 =                                 (2^3*1)+(2^2*1)+(2^1*1)+(2^0*1)
buf += parseInt("11111111", 2) + "\n"; //  255 = (2^7*1)+(2^6*1)+(2^5*1)+(2^4*1)+(2^3*1)+(2^2*1)+(2^1*1)+(2^0*1)

alert(buf);

他のやり方としては、割り算や、一旦文字列にする(非推薦)、などあります。
一旦文字列にして実行すると意味が理解しやすい場合があります。
ビット演算がやっていることが明確なのと早いそうなので良いです。
var nums, ret, i, num;
nums = new Array(0);
nums[0] = 0x2a6b2;
nums[1] = 0x296F0;
nums[2] = 12307;
nums[3] = -1;
nums[4] = 0;
nums[5] = 0x10FFFF;
nums[6] = 0x110000;
nums[7] = 65535;
nums[8] = 65536;
ret = "";
for (i = 0; i < nums.length; i++)
{
    if (nums[i] < 0)
    {
        ret = "Error";
    }
    else if (nums[i] <= 0xFFFF)
    {
        ret = "1: " + String.fromCharCode(nums[i]);
    }
    else if (nums[i] <= 0x10FFFF)
    {
        num = nums[i];
        num -= 0x10000;
        ret = "2: " + String.fromCharCode((num >> 10) | 0xD800, (num & 0x3FF) | 0xDC00);
    }
    else
    {
        ret = "Error";
    }
    alert("[" + i + "] " + nums[i] + " = " + ret);
}

0x110000 の場合 21桁目が1でその下が全部0の2進数になるので2文字とも 0xDC00 と同じになります。

正式名称は UTF-16BE でいいんだと思います。明確にするためにはBOM無しと記述すればいいのかもしれません。
http://ja.wikipedia.org/wiki/UTF-16

http://liosk.blog103.fc2.com/blog-entry-164.html?sess=83184307bdcf6494ab8d34a228efb8fe
http://en.wikipedia.org/wiki/UTF-16/UCS-2#Example_UTF-16_encoding_procedure
http://unicode.org/Public/UNIDATA/Blocks.txt

0 件のコメント: