UTF-16
Unicode の 16 ビット単位のエンコーディング方式。JavaScript や Java の内部文字列表現に使用される。
UTF-16 は Unicode を 16 ビット (2 バイト) 単位で表現するエンコーディング方式です。基本多言語面 (BMP, U+0000〜U+FFFF) の文字は 2 バイト、それ以外の文字はサロゲートペアを使って 4 バイトで表現します。JavaScript、Java、C#、Windows API など、多くのプログラミング環境の内部文字列表現として採用されています。
UTF-16 の最大の特徴はサロゲートペアの仕組みです。BMP に収まらない文字 (U+10000 以降) は、上位サロゲート (U+D800〜U+DBFF) と下位サロゲート (U+DC00〜U+DFFF) の 2 つの 16 ビットコードユニットの組み合わせで表現されます。絵文字、一部の漢字 (CJK 統合漢字拡張 B 以降)、古代文字などがサロゲートペアで表現されます。JavaScript 文字列処理の書籍で詳しく学べます。
JavaScript の文字列は内部的に UTF-16 で表現されているため、String.length は UTF-16 コードユニット数を返します。たとえば絵文字「😀」(U+1F600) はサロゲートペアで表現されるため length は 2 になります。charAt() や charCodeAt() もコードユニット単位で動作するため、サロゲートペアの片方だけを取得してしまう問題があります。ES2015 以降では codePointAt() や for...of ループでコードポイント単位の処理が可能になりました。
UTF-16 にはバイトオーダー (エンディアン) の問題があります。UTF-16BE (ビッグエンディアン) と UTF-16LE (リトルエンディアン) の 2 種類があり、ファイルの先頭に BOM (バイトオーダーマーク, U+FEFF) を付けてバイト順を識別します。Windows のメモ帳で「Unicode」として保存すると UTF-16LE (BOM 付き) になります。Web では UTF-8 が標準のため、UTF-16 のバイトオーダー問題に遭遇する機会は減っていますが、Windows 環境やレガシーシステムとの連携では依然として注意が必要です。
UTF-8 との比較では、ASCII 文字は UTF-8 が 1 バイト、UTF-16 が 2 バイトのため、英語テキストでは UTF-8 のほうがサイズ効率が良くなります。一方、CJK 文字は UTF-8 が 3 バイト、UTF-16 が 2 バイトのため、日本語や中国語のテキストでは UTF-16 のほうがコンパクトです。ただし、Web 標準が UTF-8 に統一されている現在、ファイル保存や通信には UTF-8 を使い、プログラム内部の文字列処理には UTF-16 を使うという使い分けが一般的です。プログラミングと文字コードの書籍では UTF-16 と UTF-8 の使い分けが解説されています。
文字数カウントとの関連では、UTF-16 ベースの言語 (JavaScript、Java) で文字数を数える際にサロゲートペアの扱いが最大の課題です。String.length をそのまま使うと絵文字や一部の漢字で文字数が実際より多くカウントされます。正確な文字数を得るには [...str].length (スプレッド構文) や Array.from(str).length でコードポイント数を取得するか、Intl.Segmenter で書記素クラスタ数を取得する方法があります。