文字コードについての予備知識

《 初回公開:2019/11/24 , 最終更新:未 》

文字コードにはいろいろな規格,キーワードが出てきて

文字セットと文字エンコーディング
ASCIIとShift_JISそしてCP932
UCSとUTF, UTF-8,UTF-16,UTF-32, UCS-2,UCS-4
SBCS,DBCS,MBCS
ANSIとUnicode,マルチバイト文字とワイド文字
コードポイント,コードページ
サロゲートペア
BOM

それがごっちゃになって理解が難しい。

私が理解できると思われる範囲でそれらについてまとめてみる。

【 目次 】

文字コード 文字セット 文字エンコーディング

文字コードには文字セットと文字エンコーディングがあってそれが混同されている。

文字セット(文字集合) - 文字に番号を付けて表現する

符号化文字集合(CCS,coded character set)とか言うらしい。
コンピューターは文字をそのまま認識できないので、文字に番号を付けて区別,表現する。
例えば、「あいうえお」という文字に順番を付けて「あ」は「1」,「い」は「2」とか。

asciiコード

よく知られたasciiコードの場合は「A」が16進の「41」,「B」が「42」とか

コードポイント

単純に考えると、文字に付けられた番号の事をコードポイントというのかな。

文字エンコーディング

こちらは文字符号化方式(CES,character encoding scheme)とか言うらしい。

  • 文字エンコーディングとは : JavaA2Z

    「文字エンコーディング」は、「文字コード」とほぼ同意である。
    ただし、「文字コード」はやや曖昧な用語である。
    「文字コード」は「文字の集まり」としての意味も、「変換表」としての意味も持ち、また「文字に対する整数値そのもの」を意味することもある。
    それに対して「文字エンコーディング」は「変換表」としての意味が強く、「文字の集まり」としての意味は弱い。
    また「文字に対する整数値そのもの」という意味は持たない。

これでは「文字セット」と「文字エンコーディング」の違いが良くわからない。

なぜ、バイト列に変換する必要があるかというと、コンピュータ間やファイルアクセスをおこなう場合にストリームという言葉があって

  • ストリーム (プログラミング) - Wikipedia

    ストリーム(英: stream)とは、連続したデータを「流れるもの」として捉え、そのデータの入出力あるいは送受信を扱うことであり、またその操作のための抽象データ型を指す[1]。
    出力ストリーム (output stream) を利用してデータの書き込みを行ない、また入力ストリーム (input stream) を利用してデータの読み出しを行なう。
    ファイルの入出力を扱うもの、メモリバッファの入出力を扱うもの、ネットワーク通信を扱うものなどさまざまなものがある。

このストリームを使ってデータの入出力をおこなう場合に、データをバイト列でやりとりするように統一する事で相手が何であろうと様々な入出力機器に対して一様にデータを扱う事ができる。

コンピュータ内部でのデータ処理をおこなう場合、文字列を文字セットとして扱って、外部に対して入出力をおこなう際にこの文字セットを文字エンコーディングに変換してやりとりをする方が便利なのだと思う。
また、文字エンコーディングの方式によっては転送するデータ量を削減できるというメリットもある。
Shift_JISの場合、Shift_JISそのものがバイト列になっているので文字セットと文字エンコーディングの違いを意識する必要はないようだ。

バイトストリーム

Shift_JISとCP932とコードページ

正確では無いが、文字コードのShift_JISをコードページであらわすとCP932と理解しておけば良いのかな。

Shift_JIS

  • Shift JIS(シフトJIS)とは - IT用語辞典 e-Words

    Shift JISとは、JIS規格として標準化された日本語を含む様々な文字を収録した文字コードの一つ。
    正確には「Shift_JIS」と間にアンダーバーを挟んで表記する。MS-DOSやWindowsが標準の日本語文字コードとして採用したことから広く普及した。

コードページ

  • コードページ - Wikipedia

    コードページ(英: Code page)とは、特定の符号化文字集合を指定するための数字、またはその数字で指定された符号化文字集合、あるいはそのような方法で符号化文字集合を指定するためのシステムのことである。
    CPと表示されることもある。それぞれの符号化文字集合は「コードページ○○(○○は2桁から5桁の数字)」という形で管理される。
    ...
    コードページという用語は、システムベンダ各社が管理している符号化文字集合を指す時にしか用いられず、ISO等の公的な規格の文字集合を「コードページ○○」などと呼ぶことはない。
    IBMおよび、マイクロソフトは各自、コードページを定めて管理している。

CP932

  • Microsoftコードページ932 - Wikipedia

    Microsoft コードページ 932(以下 CP932)は、マイクロソフト及び、MS-DOSのOEMベンダがShift_JISを独自に拡張した文字コードである。
    また同時に、CP932はShift_JISのWindowsアプリケーションにおける「実装」を指す用語であるとも言える。

ユニコード(Unicode)

ユニコードの文字セットはUCS(Universal Multiple-Octet Coded Character Set)と呼ばれる。

UCS-2 -> 2バイトのユニコードの文字セット
UCS-4 -> 4バイトのユニコードの文字セット

それに対して、ユニコードの文字エンコーディングはUTF(Unicode Transfer Format)。

Unicode の文字符号化方式には,主に次のようなものがあります。

  • UTF-8: 1 文字を最小 8 ビット,最大 32 ビットで表現。
  • UTF-16: 1 文字を最小 16 ビット,最大 32 ビットで表現。
  • UTF-32: 1 文字を 32 ビットで表現。

これをみるとユニコードの文字セットは固定長であるが文字エンコーディングは固定長であったり可変長であったりする。
UTF-16には更にUTF-16LEとUTF-16BEの区別があって

BOM(ByteOrderMark)とリトルエンディアン,ビッグエンディアン

サロゲートペア

サロゲートペアとは

  • Unicode - Wikipedia

    サロゲートペア(代用対)は16ビットUnicodeの領域1024文字分を2つ使い(前半 U+D800 ? U+DBFF、後半 U+DC00 ? U+DFFF)、各々1個ずつからなるペアで1024 × 1024 = 1,048,576文字を表す。
    これはちょうど16面分であり、第1面?第16面(U+10000 ? U+10FFFF)の文字をこれで表すこととした。
    加えて第0面(基本多言語面)も使用可能なので、Unicodeには合計で 1,048,576 + 65,536 - 2,048 = 111万2,064文字分の空間が確保されたことになる。
    Unicodeの符号空間が10FFFF16まで(サロゲート領域を除いて111万2064文字)とされているのはUTF-16が表現可能な限界だからである。
    サロゲートはUnicodeの符号位置の U+10000 ? U+10FFFF の範囲を16ビットユニットのペア(2つ)で表現する集合で、最初の16ビットユニットを前半サロゲートもしくはハイサロゲート、二番目を後半サロゲートもしくはローサロゲートと称する。
    ハイサロゲートは U+D800 ? U+DBFF の範囲、ローサロゲートは U+DC00 ? U+DFFF の範囲である。
    サロゲートペアはUTF-16でのみ使われ[10]、UTF-8、UTF-32ではすべての符号位置を符号化できるためこのような特別な処理は必要ない。

  • Unicode のサロゲートペアとは何か - ひだまりソケットは壊れない
  • サロゲートペア
  • 文字コードに関する覚え書きと実験

かえってわかりにくい説明になってしまうが、

サロゲートペアは2バイトUnicodeを拡張して4バイトのユニコードの文字セットの一部を表現できるようにする。
2バイトUnicodeで使用されていないU+D800 ~ U+DFFFの部分を半分に分割して、U+D800 ~ U+DBFFを上位ワード,U+DC00 ? U+DFFF上位ワードとして4バイトUnicodeを表現する。

U+D800 ~ U+DBFF で U+D800 + ( 0x00 から 0x3ff )までの 0x400(10進数で1024)
かける
U+DC00 ~ U+DFFF で U+DC00 + ( 0x00 から 0x3ff ) までの0x400(10進数で1024)

2バイトUnicodeでは 0x0000 ~ 0xffff までの 0x10000(10進数で65536)通りの文字しか表現できないのに対して、
0x10 0000(10進数で1,048,576)通りの文字
4バイトUnicodeの 0x0001 0000 ~ 0x0010 ffffの部分の文字を追加できる事になる。

その分、2バイトUnicodeのU+D800 ~ U+DFFFの部分が使用できなくなる。

サロゲートペアは正確には0xffff+1024*1024で0x10ffffの「6文字(16進数)」されると思うのだが。

サロゲートペアを使用する場合には2バイト1文字の固定長文字の原則が崩れて、4バイト1文字の場合もありうる事になる。
サロゲートペアは1バイト文字ascii に対して 日本語を表現するために2バイト文字を追加したShift_JISをイメージさせてしまう。

サロゲートペアをプログラムで処理するには

サロゲートペアの文字コードからバイナリデータへの変換規則は、
サロゲートペアの意味が理解できていれば自明の事だが。

  1. 文字コードから0x10000を引いて1番左の桁を"2"から"1"にする。これをXとする。
  2. Xを0x400で割ってその商を0xD800に足す。これを「上位サロゲート」とする。
  3. Xを0x400で割ってその剰余を0xDC00に足す。これを「下位サロゲート」とする。
  4. 上位サロゲート、下位サロゲートの順番で出力する。

サロゲートペアをプログラムで処理するには

文字セットのバイト数に関する分類

シングルバイト文字セット(Single Byte Character Sets : SBCS)

ダブルバイト文字セット(Double Byte Character Sest : DBCS)

2バイトで表現される文字。

マルチバイト文字セット(Multi-Byte Charcter Sets : MBCS)

マルチバイト文字(マルチバイトもじ)とは、

  • 1文字を複数バイトで表す体系
  • 1文字のバイト数が可変であるような体系
  • そのような体系で表される文字

を指すが、文脈により意味合いが異なる。
...
ISO 2022の体系を前提とした図形文字集合において、1文字が1バイトの文字集合(英: single-byte character set、94文字集合または96文字集合)に対して、1文字が2バイト以上の文字集合を、マルチバイト文字集合(英: multibyte character set)という。

ワイド文字セット(Wide Character Sets)

  • ワイド文字 - Wikipedia
  • マルチバイト文字 - Wikipedia

    C言語の規格において、char型以上のサイズを持つwchar_t型を利用したワイド文字(列)に対して、char型を利用して1文字あたり1バイト以上の可変長のバイト列として表したものをマルチバイト文字(列)という。
    ワイド文字に対する用語のため、1文字をもっぱら1バイトで表すシングルバイト文字であっても、この意味ではマルチバイト文字に含まれる。
    ワイド文字を内部処理に用いるプラットフォームもある。ワイド文字のサイズが2バイトあるいは4バイトの場合、本来1文字ごとに1バイトで収まるはずのASCII範囲の文字にも2バイトあるいは4バイトを費やすことになり、少なくともASCII範囲に関してはメモリ効率は劣ることになるが、処理対象のデータ中にマルチバイト文字で表現すると2バイト以上を費やすような文字が多数出現する場合は、ワイド文字を利用したほうが処理効率もメモリ効率も高くなることがある。
    ワイド文字およびマルチバイト文字の具体的な表現は環境依存であり規格には定めがない。
    これらは、実在する具体的な文字集合や符号化方式を分類する用語ではなく、固定長か可変長かという概念を定義した用語である。
    ...
    Microsoft Windowsでは、ワイド文字が2バイト(16ビット)として定義されており、符号化方式にUTF-16を利用する。

C,C++にはwchar_tというワイド文字型というのが存在する。
マルチバイト文字が可変長なのに対して、
ワイド文字は固定長の文字で、環境により文字のサイズが2バイトと4バイトの場合がある。
Windows(Microsoftの開発言語)では2バイトであるのに対して、GNU C Library (glibc)では4バイトとなっているようである。

その他の文字コードに関する参考サイト

プログラミングにおけるワイド文字に対するマルチバイト文字の位置づけ

一応、マルチバイト文字は可変長の複数バイトの文字列となっているのだが、
VC++等ではユニコード文字列を処理するのワイド文字を示すwchar_t型が、ユニコード以前のShift_JISやascii文字を処理するのに従来からあるchar型が使われる。
そのため、ワイド文字は2バイト(または4バイト)の固定長でユニコードを、それに対してマルチバイト文字はワイド文字以外のシングルバイト文字とダブルバイト文字もマルチバイト文字に含まれるという位置付けになっている。

VC++における文字型の扱い

VC++において、日本語の文字を扱うにあたり、文字コードに対する知識(認識)が必要だったので文字コードに関する情報を集めてきた。
これらの知識があれば下記の参考サイトのVC++における文字型の扱いが理解できると思う。

Windowsのシステム情報の取得のサンプルコードをみると他の処理系ではみかけないようなマクロ頻繁に登場する。

TCHAR _tprintf TEXT

これはいったい何だろう?

ANSI

Windowsではワイド文字はunicode、対するマルチバイト文字の事をANSIと呼んだりする。

そして、Windows APIにはANSI用の関数とunicode用の関数が2つが存在する。
例えば、GetUserName APIにはANSI用のGetUserNameAとunicodeのGetLastNameWのように関数名の最後に A または W の文字が付けられて区別されている。

  • コンパイルとリンク方法 - Web/DB プログラミング徹底解説

    また、GetLastNameA のように、関数名の最後に A または W が付いている場合はそれはそれぞれ次のように解釈します。
    A は、Non-Unicode ビルド用の関数であることを示します。ANSI ビルドなどと言われます。 W は Unicode ビルド用の関数です。
    Windows の API はほぼ全てマクロとして実装されており、同じ API を使っているつもりでもコンパイルの仕方によって (コンパイルオプションによって)、 ANSI ビルド用のコード、あるいは Unicode ビルド用のコードとしてコンパイルされます。
    何も指定しないと、ANSI ビルドになります。

TCHAR型 - ワイド文字とマルチバイト文字両対応のコードを記述する。

  • 今さら聞けない、教えてもらえない!! Unicode /マルチバイト文字対応 国際化VC ++ プログラミングの基礎!! – JAPAN Platform SDK(Windows SDK) Support Team Blog

    プログラム上で Unicode はワイド文字として扱われています。
    マルチバイト文字セット (Multi-Byte Charcter Sets : MBCS) とは、「多バイト文字」と表現されて「2 バイト以上の文字」を指すこともありますが、MSDN の規定では「1 バイトおよび 2 バイト文字」となっております。シングルバイト文字もダブルバイト文字もマルチバイト文字に含まれます。Shift-JIS はここに含まれます。
    VC++の場合のマルチバイト文字とワイド文字の関係
    ...
    Unicode として文字を扱う場合とマルチバイトとして文字を扱う場合は文字の ”型” が異なります。
    しかし、文字セットの種類を気にせずに使用できる “型” がございます。
    それは・・・
    「TCHAR 型、LPCTSTR 型、LPTSTR 型」です。
    TCHAR 型、LPCTSTR 型、LPTSTR 型は Visual Studio 2005 ( Visual C++ 2005 ) 以降のバージョンでビルドする際、Unicode ビルドを選択いたしますとワイド文字型を指定しなくても自動的にワイド文字型として扱われ、MBCS (マルチバイト) ビルドを選択いたしますと自動的に char 型として扱われます。
    「Unicode 文字セットを使用する」に設定している場合
    ・ TCHAR = WCHAR
    ・ LPCTSTR = const WCHAR
    ・ LPTSTR = WCHAR

    「マルチバイト文字セットを使用する」に設定している場合
    ・ TCHAR = char
    ・ LPCTSTR = const char
    ・ LPTSTR = char

  • Visual C++雑多メモ ー TCHAR編

    Visual C++は、ワイド文字(Unicode)/マルチバイト文字のプログラムコードを、デファイン_UNICODEおよびUNICODEの制御だけで切り替えられるようにする仕掛け(本記事ではTCHARと呼称)を用意しています。
    Windows のAPIやVisual C++の標準ライブラリはほとんどがこのTCHARの仕掛けを使っています。

以上の事を整理すると

呼称 文字コード
マルチバイト文字 , ANSI Shift_JIS(CP932) char
ワイド文字 , Unicode Unicode wchar_t

コマンドラインからのコンパイルでの文字セットの指定

VC++のUnicode対応のプログラムはUNICODE と _UNICODE の二つの定数を指定する。

ANSI ビルド

cl /c test.cpp
link advapi32.lib test.obj

Unicode ビルド

cl /DUNICODE /D_UNICODE /c test.cpp
link advapi32.lib test.obj

なぜUNICODEと_UNICODEの両方なのか

  • TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE | The Old New Thing

    アンダースコアのないプレーンバージョンは、Windowsヘッダーファイルがデフォルトとして扱う文字セットに影響します。
    したがって、UNICODEを定義すると、GetWindowTextは、たとえばGetWindowTextAではなくGetWindowTextWにマップされます。
    同様に、TEXTマクロは「…」ではなく「L」…」にマッピングされます。
    下線付きのバージョンは、Cランタイムヘッダーファイルがデフォルトとして扱う文字セットに影響します。
    したがって、_UNICODEを定義すると、_tcslenは、たとえばstrlenではなくwcslenにマップされます。
    同様に、_TEXTマクロは、「…」ではなくL「…」にマッピングされます。

  • Unicode 対応 - Windows API 入門

    ソースコード中の識別子 MessageBox は,UNICODE が定義されていれば MessageBoxW に,未定義であれば MessageBoxA に置換されます。
    ...
    このように,文字列を扱う Windows API 関数の多くは UNICODE の定義の有無に応じて関数名の置換を行っています。
    また,Visual C++ のランタイムライブラリも,同様にして _MBCS, _UNICODE の定義の有無によって,関数名の置換を行っています。

  • ホイール欲しい ハンドル欲しい » _T() と TEXT() の違いやソースの文字コード

BOMなしUTF-8で保存した日本語を含むソースをコンパイルしようとした時に「warning C4819」のコンパイルエラーとなる事があるので注意。

Visual Studio IDE(Visual Studio Community 2019)での文字セットの指定

Visual Studio IDE(Visual Studio Community 2019)で文字セットを指定するには、リソリューションエクスプローラよりプロジェクトを選択してマウスの右クリックメニューよりプロパティを選択、下記画面のように詳細より文字セットにて設定。

Visual Studio IDE(Visual Studio Community 2019)で文字セットを指定


日本語をコンソール出力する場合はshift_jisでないとコマンドプロンプトがCP932のため文字化けするので注意、当たり前だけど。

char8_t型 char16_t型 char32_t型

標準のC/C++ではchar8_t型 char16_t型 char32_t型が定義されている。

  • 文字列リテラル | C++11 and C++14 additional features handbook.

    char16_t, char32_tは、それぞれUCS2の範囲の文字、UCS4の範囲の文字を扱うことができる。
    UCSとUnicodeはほぼ互換であるため、UTF-16, UTF-32にそれぞれ対応すると考えれば良い。
    なお、UTF-16の場合はサロゲートペアを用いる場合がある。
    よってchar16_t型の文字列の場合、サロゲートペア形式を用いて複数の値(例えば2文字分)を1文字として表現する可能性もある。(UCS2は固定幅文字のみであり、UTF-16はマルチバイト文字も扱う。)
    また、char16_t型文字列、char32_t型文字列は、Unicodeとの互換性を保つために、0x0 - 0x10FFFFまでのコード値のみで構成されなければならない。

  • char16_tとchar32_t - cpprefjp C++日本語リファレンス

    ただし、C++11時点で、標準ライブラリではchar16_tとchar32_tの入出力はサポートしない。そのため、それらの文字・文字列はシステムの文字コードに変換して入出力する必要がある。
    ...
    uプレフィックスが付く文字リテラルの型はchar16_tであり、
    ...
    Uプレフィックスが付く文字リテラルの型はchar32_tであり、

  • UTF-8エンコーディングされた文字の型としてchar8_tを追加 - cpprefjp C++日本語リファレンス
  • ワイド文字 - Wikipedia

VC++でもchar8_t型 char16_t型 char32_t型は使えるみたい。

参考までに、Visual C++ 2010ということであれば... 扱うことができる文字の表現方法は少なくとも5種類あります。

  1. ナロー文字。ASCIIなど、1バイトで表現出来る文字
  2. 多バイト文字。シフトJISなど、2バイト以上で1文字を表す。
  3. ワイド文字。wchar_t型を使用し、UTF-16で表現
  4. char16_t型を使用し、UCS-2で表現
  5. char32_t型を使用し、UCS-4で表現

C++0xを部分的にサポートしたので、この辺はかなり複雑になりました。

c++ auto

サンプルコードをみていたらautoというコードが、
余談だがC++でも型推論が使えるみたいで。

蛇足

Delphiの文字型

参考までにDelphiの文字型についてのリンクを

その他の参考サイト


文字コードの扱いはごちゃごちゃしていて、
何だか更に混乱させる結果になってしまったのかも知れない。

ページのトップへ戻る