21 人が閲覧(直近 30 日) Webサイト

ISO/IEC 10646(UCS)とは?Unicodeとの違いとUTF-8/16/32・文字コードを実測で解説

ISO/IEC 10646(UCS)とは?Unicodeとの違いとUTF-8/16/32・文字コードを実測で解説

ISO/IEC 10646は、ISO(国際標準化機構)とIEC(国際電気標準会議)が共同で定めた文字コードの国際規格で、別名をUCS(Universal Coded Character Set)といいます。世界中の文字にU+0000からU+10FFFFまでの符号位置を割り当てる規格で、収録文字と符号化形式はUnicodeと同期しています。ただし「同期している」ことと「同じ規格である」ことは別です。この記事では、両者の役割分担、UTF-8・UTF-16・UTF-32が実際に何バイトになるかの実測、Shift_JISとの変換で起きる文字化けの再現、そして日本のJIS X 0221が今どの版に対応しているかまでを、確認できる一次情報だけで整理します。

まとめ:ISO/IEC 10646(UCS)の要点

先に結論を押さえます。

  • 現行版はISO/IEC 10646:2020(第6版)。1993年のISO/IEC 10646-1:1993が初版で、2020年12月発行の第6版に追補としてAmd 1:2023、Amd 2:2025が加わっています。担当委員会はISO/IEC JTC 1/SC 2です。
  • UnicodeとISO/IEC 10646は符号位置と符号化形式だけが同期している。正規化・双方向処理・文字プロパティといった実装ルールはUnicode側にしかありません。実装者が読むべきはUnicodeの仕様書です。
  • 符号空間は17面×65,536で1,114,112個。Unicode 17.0(2025年9月9日公開)で収録されているのは159,801字なので、埋まっているのは14.3%です。
  • UTF-8・UTF-16・UTF-32はどれも同じ文字を表す別の入れ物。日本語1文字はUTF-8で3バイト、UTF-16で2バイト、Shift_JIS(cp932)で2バイトになります。新規設計ではUTF-8を選べば足ります。
  • UCS-2は現行規格から削除済み。ISO/IEC 10646:2011以降の版には記載がなく、Unicodeの仕様書も廃れたものとして扱っています。設計書に「UCS-2」と書くのは避けてください。
  • 日本のJIS X 0221:2020はISO/IEC 10646:2017に対応。ISO側の現行第6版(2020年)には追随していないため、JISだけを根拠に最新仕様を語ることはできません。

ISO/IEC 10646の定義と現行の版数

ISO/IEC 10646は、それまで国や言語ごとにばらばらだった文字コードを、単一の符号空間に統合するために作られた規格です。規格名にあるUCS(Universal Coded Character Set)は、この規格が定義する文字集合そのものを指します。規格が定めているのは大きく二つで、どの文字にどの符号位置を割り当てるかという「符号化文字集合」と、その符号位置をバイト列にどう写すかという「符号化形式」です。

版の履歴は、単一規格として整理された経緯を反映して枝分かれしています。1993年のISO/IEC 10646-1:1993が最初の版で、その後10646-1と10646-2に分かれていた時期を経て、2003年版で1本の規格に統合されました。以降は2011年版、2012年版、2014年版、2017年版と改訂が続き、現在の最新は2020年12月に発行されたISO/IEC 10646:2020(第6版)です。第6版に対しては追補が2件出ており、Amd 1:2023が2023年7月、Amd 2:2025が2025年6月に発行されています。

符号位置の範囲はU+0000からU+10FFFFです。この上限は途中で拡張されたものではなく、UTF-16で表現できる範囲に合わせて確定した値で、以降の版でも変わっていません。「ISO/IEC 10646は4バイトで約21億文字を扱える」という説明を見かけることがありますが、これは2003年より前のUCS-4の設計思想を指したもので、現行版の符号空間ではありません。

UnicodeとISO/IEC 10646の違いと役割分担

この2つは競合規格ではなく、ISO/IEC JTC 1/SC 2/WG 2とUnicodeコンソーシアムが調整しながら並行して維持しています。Unicodeコンソーシアムの公式FAQは、両者の関係を次のように述べています。

the character codes and encoding forms are synchronized between Unicode and ISO/IEC 10646(文字コードと符号化形式はUnicodeとISO/IEC 10646の間で同期している)

同期しているのは符号位置と符号化形式だけです。同じFAQは、Unicodeが追加で持っているものをこう説明しています。

it supplies an extensive set of functional character specifications, character data, algorithms and substantial background material that is not part of ISO/IEC 10646(Unicodeは、ISO/IEC 10646には含まれない広範な機能仕様・文字データ・アルゴリズム・背景資料を提供する)

実務上の差はここに集約されます。両規格の規定範囲を整理すると次のようになります。

項目 ISO/IEC 10646 Unicode
符号位置の割り当て 規定する 規定する(同一)
UTF-8/UTF-16/UTF-32 規定する 規定する(同一)
文字プロパティ(字種・方向・大小) 規定しない 規定する
正規化(NFC/NFD/NFKC/NFKD) 規定しない 規定する(UAX #15)
双方向テキスト処理 規定しない 規定する(UAX #9)
照合順序・大文字小文字変換 規定しない 規定する
異体字セレクタの登録簿(IVD) 規定しない 運用する(UTS #37)
入手 ISOから有償 unicode.orgで無償公開

アプリケーションを実装する側にとって必要な情報は、ほぼすべてUnicode側にあります。文字列の比較で「が」と「か+濁点」を同一視したい、氏名欄の全角と半角を揃えたい、といった要件は正規化の話であり、ISO/IEC 10646には答えが書かれていません。規格名を要件定義書に書くならISO/IEC 10646で構いませんが、実装の根拠として参照するのはUnicodeの仕様書とUAX/UTSです。多言語対応の設計全体を見るときはi18n(国際化)とは何か?Angularアプリでの必要性と基本的な仕組み【ローカライズとの違いも解説】もあわせて確認してください。

UCSの符号空間と基本多言語面(BMP)

UCSの符号空間は17個の「面(plane)」に分かれています。1面あたり65,536個の符号位置があり、17面で1,114,112個です。このうち第0面がBMP(Basic Multilingual Plane、基本多言語面)で、範囲はU+0000からU+FFFFです。ラテン文字、キリル文字、ひらがな・カタカナ、常用漢字を含むCJK統合漢字の主要部分は、すべてBMPに収まっています。

第1面以降は補助面と呼ばれ、絵文字(U+1F600台)、古代文字、CJK統合漢字拡張領域などが置かれています。日常的な日本語処理でも、環境依存文字として扱われがちな一部の漢字は補助面にあります。

符号空間のうちU+D800からU+DFFFまでの2,048個は「サロゲート領域」として予約されており、単独では文字を表しません。UTF-16で補助面の文字を表すための部品として使う領域で、RFC 3629はUTF-8についてこう明記しています。

The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF(UTF-8の定義はU+D800からU+DFFFの符号位置を符号化することを禁じている)

収録字数の増え方も押さえておくと版の判断がしやすくなります。Unicode 17.0は2025年9月9日に公開され、4,803字が追加されて合計159,801字になりました。符号空間1,114,112個に対する充足率は14.3%で、枯渇を心配する段階ではありません。次のUnicode 18.0は2026年9月16日の公開が予定されており、Chisoi、Jurchen、Seal、Proto-Cuneiformの4字種が新規に追加される見込みです。

UTF-8・UTF-16・UTF-32のバイト数と使い分け

UTF-8・UTF-16・UTF-32は、同じ符号位置を別のバイト列に写す3つの符号化形式です。どれを使っても表せる文字の集合は同じで、変わるのはバイト数と処理のしやすさだけです。

各方式のバイト数の実測

Python 3.9.6で代表的な文字を各方式に符号化し、16進表記とバイト数を出力した結果が次のとおりです。

>>> for enc in ["utf-8", "utf-16-le", "utf-32-le", "cp932"]:
...     b = "あ".encode(enc)
...     print(enc, b.hex().upper(), len(b))
utf-8 E38182 3
utf-16-le 4230 2
utf-32-le 42300000 4
cp932 82A0 2

主要な文字について同じ測定を行うと、方式ごとの性格がはっきりします。

文字 符号位置 UTF-8 UTF-16LE UTF-32LE cp932
A U+0041 41(1バイト) 4100(2バイト) 41000000(4バイト) 41(1バイト)
あ U+3042 E38182(3バイト) 4230(2バイト) 42300000(4バイト) 82A0(2バイト)
漢 U+6F22 E6BCA2(3バイト) 226F(2バイト) 226F0000(4バイト) 8ABF(2バイト)
𩸽 U+29E3D F0A9B8BD(4バイト) 67D83DDE(4バイト) 3D9E0200(4バイト) 変換不可
絵文字 U+1F600 F09F9880(4バイト) 3DD800DE(4バイト) 00F60100(4バイト) 変換不可

読み取れることは3点です。第一に、ASCII文字はUTF-8では1バイトのままなので、HTMLやJSONのようにタグ・キー名が英数字中心のデータではUTF-8が最も小さくなります。第二に、日本語の本文だけを比べるとUTF-8は3バイト、Shift_JISは2バイトで、UTF-8のほうが1.5倍になります。「UTF-8はサイズ効率が良い」という説明は英数字を含む実データ全体での話であり、日本語のみを取り出せば逆転します。第三に、UTF-8の最大長は4バイトです。RFC 3629はcharacters from the U+0000..U+10FFFF range are encoded using sequences of 1 to 4 octets(U+0000からU+10FFFFの文字は1から4オクテットの列で符号化される)と規定しており、5バイト・6バイトのUTF-8は2003年11月のRFC 3629で正式に廃止されています。

サロゲートペアで文字数が合わなくなる理由

補助面の文字をUTF-16で表すときは、サロゲート領域の値を2つ並べます。U+29E3D(𩸽)を測定すると次のようになります。

>>> b = "𩸽".encode("utf-16-le")
>>> [hex(int.from_bytes(b[i:i+2], "little")) for i in range(0, len(b), 2)]
['0xd867', '0xde3d']

1文字がD867とDE3Dという2つの16ビット単位に分かれています。これがサロゲートペアです。文字列を内部でUTF-16として保持する言語では、この1文字が長さ2として数えられます。Node.jsで確認すると次のようになります。

$ node -e 'const s = "\u{29E3D}"; console.log(s.length, [...s].length)'
2 1

JavaScriptのlengthは2を返し、符号位置で数え直すと1になります。JavaやC#のようにUTF-16を内部表現に採用した言語でも同じ現象が起きます。氏名入力欄で「20文字まで」と制限したつもりが、特定の漢字や絵文字を含むと10文字で弾かれる、という不具合はここが原因です。文字数制限を実装するときは、符号位置で数えるのか符号単位で数えるのかを仕様として明記してください。

UCS-2を設計書に書いてはいけない理由

UCS-2は、すべての文字を固定2バイトで表すという前提で作られた古い符号化形式です。補助面の文字を表現できないため役目を終えており、Unicodeの仕様書は次のように記述しています。

UCS-2 should now be considered obsolete. This documentation has been removed from ISO/IEC 10646:2011 and subsequent editions.(UCS-2は現在では廃れたものとみなすべきである。この記述はISO/IEC 10646:2011および以降の版から削除された。)

つまり現行規格にUCS-2という符号化形式は存在しません。日本語の解説記事や用語辞典には、UCS-2を現役の符号化形式としてUTF-8・UTF-16と並記しているものが残っていますが、2011年版以降の規格を根拠にすると誤りです。一方で、データベース製品には「UCS-2」という名前の文字セットが今も存在します。これは製品側が過去の名称を維持しているだけで、実体はUTF-16相当であることが多く、規格名と製品の設定値名を混同しないよう注意が必要です。

新規に設計する場合の判断は明快です。特別な理由がなければUTF-8を選んでください。UTF-16を選ぶ合理性は、JavaやWindows APIのように既存プラットフォームの内部表現がUTF-16で、変換コストを避けたい場合に限られます。UTF-32は1文字4バイト固定で扱いは単純ですが、英数字中心のデータでサイズが4倍になるため、保存形式や通信形式としては現実的ではありません。文字単位の走査を高速化したい処理の内部表現として一時的に使う程度です。

Shift_JIS・EUC-JPとの違いと主要な文字コード一覧

Shift_JISやEUC-JPは、UCSとは無関係に日本語だけを対象として設計された符号化方式です。同じ「文字コード」という言葉で呼ばれますが、世界中の文字を単一の符号空間に収める設計とは出発点が違います。主要な方式を整理すると次のようになります。

名称 対象 日本語1文字 符号空間 現在の位置づけ
UTF-8 UCS全体 3バイト U+0000〜U+10FFFF Web・Linuxの既定
UTF-16 UCS全体 2バイト(補助面は4) U+0000〜U+10FFFF Java・Windows APIの内部表現
UTF-32 UCS全体 4バイト U+0000〜U+10FFFF 内部処理用に限定
UCS-2 BMPのみ 2バイト U+0000〜U+FFFF 2011年版で規格から削除
Shift_JIS(cp932) 日本語 2バイト JIS X 0208+機種依存文字 レガシー資産・一部の行政系
EUC-JP 日本語 2バイト JIS X 0208中心 旧UNIX系のレガシー
ISO-2022-JP 日本語 可変(エスケープ切替) JIS X 0208中心 メール本文の旧慣行
ASCII 英数字記号 非対応 0x00〜0x7F UTF-8の部分集合として存続

Shift_JISからUTF-8への移行で問題になるのは、サイズやバイト数ではなく収録範囲の非対称性です。UTF-8はShift_JISの文字をすべて表せますが、逆は成り立ちません。前掲の測定でU+29E3D(𩸽)がcp932に変換できなかったのがその例です。この文字は水産物名や人名に現れるため、業務システムでは実際に発生します。UTF-8で受け取ったデータをShift_JISのレガシーシステムへ渡す連携では、変換不可の文字をどう扱うか(エラーにする、代替文字に置換する、外字領域に割り当てる)を必ず設計時に決めてください。基幹連携でファイル転送を挟む構成についてはHULFTとは?ファイル転送の仕組みと暗号化・ジョブ連携の設計とiPaaS移行の判断基準を解説で扱っています。

もう一つの落とし穴は、いわゆる機種依存文字です。cp932はJIS X 0208に加えてNEC特殊文字とIBM拡張文字を含み、丸数字やローマ数字が重複して2箇所に定義されています。この重複のため、Shift_JISからUTF-8へ変換して再度戻すと元のバイト列に一致しないことがあります。往復変換で同一性が保たれる前提でデータ移行を設計すると、照合処理で不一致が出ます。

文字化けの再現パターンと原因の切り分け

文字化けは、バイト列そのものが壊れて起きるのではなく、正しいバイト列を誤った符号化方式で解釈したときに起きるのがほとんどです。したがって化け方のパターンから原因を逆算できます。「パスワード」という文字列を、保存時と読み取り時で方式を食い違わせたときの出力が次のとおりです。

>>> text = "パスワード"
>>> text.encode("utf-8").decode("cp932", errors="replace")
'繝代せ繝ッ繝シ繝�'
>>> text.encode("cp932").decode("utf-8", errors="replace")
'�p�X���[�h'
>>> text.encode("utf-8").decode("latin-1")
'ã\x83\x91ã\x82¹ã\x83¯ã\x83¼ã\x83\x89'

3番目の出力に現れる\x83のような表記は、Latin-1として読んだ結果に画面へ表示されない制御文字が混じることを示しています。実際の画面では制御文字が見えないため「ãã¹ã¯ã¼ã」のように読めます。

出力の見た目と原因の対応をまとめると、切り分けが機械的にできます。

化け方の特徴 実際の出力例 原因 対処
「繝」「縺」「蜈」が並ぶ 繝代せ繝ッ繝シ繝� UTF-8をShift_JISとして読んだ 読み取り側をUTF-8に変更
半角英字と記号が混じる �p�X���[�h Shift_JISをUTF-8として読んだ 読み取り側をcp932に変更
「Ã」「ã」などの欧文が並ぶ ãã¹ã¯ã¼ã(間に不可視の制御文字) UTF-8をLatin-1として読んだ Content-Typeのcharset指定を修正
先頭だけに謎の記号 行頭の見えない3バイト UTF-8のBOM(EF BB BF) BOMなしUTF-8で保存し直す
特定の文字だけが「?」 丸数字やローマ数字が? 変換先に該当文字がない 変換不可時の扱いを設計で決める

1行目の「繝代せ繝ッ繝シ繝�」は、UTF-8のバイト列をShift_JISの2バイト文字として読み直した結果です。UTF-8の日本語は3バイトなので、2バイト単位で区切ると境界がずれ、E3で始まる並びが「繝」に、E7からE9で始まる並びが「縺」や「蜈」に落ちます。末尾の「�」は、区切りが合わずに1バイト余ったことを示しています。逆に言えば、「繝」や「縺」が見えたら読み取り側の設定を疑えばよく、データが壊れているわけではありません。バイト列は無事なので、正しい方式で読み直せば完全に復元できます。

3行目のようにアクセント付きラテン文字が並ぶ場合は、HTTPレスポンスヘッダやHTMLのmeta要素でcharsetがLatin-1(ISO-8859-1)と宣言されている、あるいは宣言そのものが無くブラウザが推測に失敗しているケースが典型です。

発生源としては、データベースの初期化時に設定した文字コードが後から効いてくるパターンが多く見られます。PostgreSQLではinitdb実行時に決まるエンコーディングが後から変更できません。構築段階で指定しておく必要があります。詳細はPostgreSQLのインストール手順|Windows・Ubuntu別の導入とinitdbで決まる文字コードを参照してください。OSのロケール設定に起因する表示崩れについてはUbuntuとは|インストール手順・推奨スペック・日本語化と文字化け対策を初心者向けに解説で扱っています。

JIS X 0221と異体字(IVS)の現状

日本国内でISO/IEC 10646に対応するJIS規格がJIS X 0221「国際符号化文字集合(UCS)」です。ここで押さえておくべきなのは、JIS側とISO側で版がずれている点です。

現行のJIS X 0221:2020は2020年11月20日に改正され、2025年10月20日に確認(内容を変えずに有効性を継続する手続き)を受けています。対応国際規格として記載されているのはISO/IEC 10646:2017とその追補2件で、一致の程度はIDT(一致)です。つまりJIS X 0221:2020が写しているのはISOの2017年版であり、ISO側の現行第6版(2020年)ではありません。公共調達の仕様書などでJIS X 0221を引用する場面はありますが、収録文字の最新状況を語る根拠としては使えないことになります。

人名や地名で問題になる異体字については、UCSの符号位置そのものではなくIVS(Ideographic Variation Sequence、異体字列)という仕組みで扱います。仕様はUnicodeのUTS #37で定義され、最新はバージョン7.0(2026年4月30日)です。UTS #37はIVSを次のように定義しています。

An Ideographic Variation Sequence (IVS) is a sequence of two coded characters, the first being a character with the Ideographic property that is not canonically nor compatibly decomposable, the second being a variation selector character in the range U+E0100 to U+E01EF.(IVSは2つの符号化文字の列であり、1つ目は正規分解も互換分解もされない表意文字、2つ目はU+E0100からU+E01EFの範囲の異体字選択符号である。)

要点は、異体字は新しい符号位置を割り当てるのではなく、基底の漢字1文字に見えない選択子を後置して区別するという設計です。選択子の範囲U+E0100からU+E01EFは240個なので、1つの漢字につき最大240種類の字形を区別できます。どの組み合わせが有効かはIVD(Ideographic Variation Database)に登録されたコレクションで決まり、最新版は2026年8月3日版です。登録コレクションはAdobe-Japan1、CAAPH、Hanyo-Denshi、KRName、Moji_Joho、MSARGの6つで、行政システムで参照されることが多いのは文字情報基盤に由来するMoji_Johoです。

実装上の注意は2点あります。第一に、IVSは見えない文字を含む2文字の列なので、文字数のカウントや部分一致検索が素朴な実装では破綻します。第二に、IVSに対応したフォントがなければ選択子は無視され、基底の字形で表示されます。字形が正しく出ないという不具合の切り分けでは、データ側の問題かフォント側の問題かを先に分けてください。多言語・多地域向けの製品展開全体で何を対象にするかの判断はローカライズとは?翻訳・国際化との違いと対象範囲・実施可否の判断基準で整理しています。

よくある質問

ISO/IEC 10646とUnicodeの違いは何ですか?

符号位置の割り当てとUTF-8・UTF-16・UTF-32の定義は両者で同期しており、この範囲に違いはありません。違うのは規定範囲で、文字プロパティ、正規化、双方向処理、照合順序といった実装ルールはUnicodeにしかありません。ISO/IEC 10646はISOから有償で購入する必要がありますが、Unicodeの仕様書はunicode.orgで無償公開されています。実装の根拠として参照するならUnicode側です。

UTF-8とShift_JISの違いは何ですか?

対象範囲が違います。UTF-8はUCS全体を表せますが、Shift_JIS(cp932)は日本語を中心とした限られた文字集合しか表せません。このためUTF-8からShift_JISへの変換は情報が落ちる可能性があり、逆方向は安全です。バイト数の比較と変換不可文字の実例は本文の表で示しています。

UTF-8とUTF-16はどちらを選ぶべきですか?

新規に設計するならUTF-8です。UTF-16を選ぶ理由は、JavaやWindows APIのようにプラットフォームの内部表現がUTF-16で変換コストを避けたい場合に限られます。UTF-16はバイト順(BE/LE)の区別が必要で、BOMの有無やヘッダ設計の考慮点が増えます。UTF-8はバイト順の概念がなくASCIIと互換なので、保存形式・通信形式としての扱いが簡単です。

UCS-2は今も使えますか?

規格としては使えません。BMPの範囲しか表せず、補助面にある一部の漢字や絵文字を扱えないためです。削除された経緯と、製品の設定値名として残っている場合の扱いは本文で説明しています。

Unicodeは1文字何バイトですか?

Unicodeは符号位置を定める規格なので、それ自体にバイト数はありません。バイト数が決まるのは符号化形式を選んだ時点です。日本語の「あ」(U+3042)であればUTF-8で3バイト、UTF-16で2バイト、UTF-32で4バイトになります。「Unicodeは2バイト」という説明は、UCS-2しかなかった1990年代の前提が残ったもので、現在は正しくありません。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  3. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動
  4. 2026.10.09 テックブログ スタディサプリの不正アクセスとメールアドレス3,687件|アカウント列挙を防ぐ実装
  5. 2026.01.14 テックブログ CGLA(シーグラ)とは?Lenzo(レンゾ)の半導体技術と上場・株の最新情報【2026年版】

RELATED POSTS 関連記事

目次