◆ U+XXXX は 5 桁か 6 桁のがある
◆ サロゲートペアは前半・後半の範囲がわかれていて 2バイトで 1 文字のところとも被らない範囲
    ◆ 前半 U+D800 ~ U+DBFF
    ◆ 後半 U+DC00 ~ U+DFFF
◆ サロゲートペアの範囲は U+10000 ~ U+10FFFF で表記
    ◆ 前半後半の 4 桁の値は計算して求められる

JavaScript でも ES6 で 4 バイト文字が扱いやすくなったのでなにかやろうかと調べていると 気になったことが

U+1F31F と \uD83C\uDF1F の 2 種類あるけどどうなってるの?

Unicode

Unicode は文字コードが色々ありすぎて面倒なので統一したもの作ろうとかで作られた いろんな国の全部の文字が存在する文字コード   とかだったはず

もともとは全部の文字を 2 バイトで表して

U+0000 ~ U+FFFF

におさまるはずだったけど 色々増えてきたので 結局増やして 4 バイトとかでてきてる

U+0000 の 16 進数 4 桁は 4bit * 4 = 16bit = 2byte

Unicode の符号化

文字とそれに対する U+XXXX というのがあっても 実際にバイナリデータと文字をどう対応つけるかです
UTF-8 とかいろいろあるけど 全部 Unicode で U+XXXX 表現は一緒

UTF-16 が半角のアルファベットであっても全部 2 バイトで表す U+XXXX と対応してる基本のやつ

UTF-7 は UTF-16 を base64 通して 7 バイトで表したもの

UTF-8 はこれまでの主流な ASCII に合わせて ASCII 部分なら一緒
日本語は 3 バイトになって Shift-JIS や UTF-16 よりバイト数が多い
UTF-8 を含めて Shift-JIS など多くの文字コードが ASCII 部分が一緒だから 半角英数字だけのソースコードとかではこれまでと互換性があって良いのですけど エディタで開いたときに文字コードチェックする最初の部分が全部英数字のせいで 違う文字コードで開かれて困ることも……
そんなときのために BOM なんて便利なものもあるのですが使うと問題起きることもあるので (PHP とか PHP とか PHP とか) これはあまり使われてなさそう

UTF-16 のコードは 本文中に文字コードが書かれる HTML などで文字コードが書いている部分自体を読めない問題があったり……
一応 UTF-16 でも "00" がついてるだけで ASCII 部分は UTF-8 のと一緒だったりします

一般的の人でも役立つ知識は 英語が多いメモは UTF-8 で日本語が多いなら UTF-16 にしたら保存した時のサイズが小さくできるというのがあります
ですが 今の時代ではテキストサイズなんて気にしなくていいので あまり必要ないです


という感じ
他にも 32 とかあるけど略!

サロゲートペア

文字数が足りなくなったので 昔ながらの使ってないところを組み合わせる方法で 使える文字数を増やしてます
増えた分の文字は 2 バイトを 2 つで 4 バイトです

範囲は前半と後半が別の範囲で

前半 : U+D800 ~ U+DBFF
後半 : U+DC00 ~ U+DFFF

になっています
前半を U+D800 ~ U+DFFF にして 後半を U+0000 から U+FFFF までにしたほうが使える量は増えるのですが これまでのことから 2 バイトで 1 文字の文字と重なると 前半か後半の片方の文字だけ見て特殊な制御文字と判断してしまったり 2 バイトずれたせいで全部が文字化けになったりと扱いづらくなるので こんなことになってるみたい

前半と後半を分けてるのも 部分的に取り出したのが 後半バイトからだったとしても 後半+前半 でくっつけて文字化けにならないようにということなんだと思います


データ的には
"\uD83C\uDF1F"
のようになります

5桁と6桁の U+XXXX

サロゲートペアの前半と後半はそれぞれ 1024 文字で合計は 1048576 文字
0000 から FFFF は 65536 で 65536 * 16 = 1048576 です

0000 から FFFF が 16 個分なので
10000 ~ 10FFFF にするとちょうどぴったりです

なので "U+D83CDF1F" のような 8 文字ではなく 5 文字と 6 文字で表すようになってます

JavaScript

JavaScript では 8 文字で書くのも 5 文字で書くのもできます
"\uD83C\uDF1F" === "\u{1f31f}"
// true

書き方は 5文字だと { } が必要です
ないと 4 文字のところで切れます
"\u0061"
// "a"
"\u{0061}"
// "a"

"\u1f31f"
// "ἱf"


U+XXXXX 形式から 8 文字のサロゲートペアに変換は規則的なので簡単に求められます
function sarope(uni){
var x = uni - 0x10000
var upper = ~~(x / 0x400) + 0xd800
var lower = ~~(x % 0x400) + 0xdc00
console.log("upper", upper, upper.toString(16))
console.log("lower", lower, lower.toString(16))
console.log("unicode", uni, uni.toString(16))
console.log(String.fromCodePoint(uni))
}
sarope(0x1F31F)
upper 55356 d83c
lower 57119 df1f
unicode 127775 1f31f
🌟

これで 4 バイト文字も怖くない!