文字コードについて調べたことや実験したこと, テストプログラム,データファイルなどを随時掲載する予定です. ただし筆者の理解不足や誤解により誤りがあるかもしれませんので, ご利用は自己責任で.
このページの主な更新は Blog でお知らせします.
表示確認ブラウザ:FireFox 3.6,IE8.
標準的なシフト JIS (以下 Shift_JIS) が扱う2バイト文字は,JIS X 0208 の 1〜94 区であるが,ベンダ独自拡張版のシフト JIS (Windows-31J:Microsoft のコードページ 932 (以下 CP932) など) では,JIS X 0208 には存在しない 95〜120 区も扱う.
シフト JIS において,2バイト文字の第1バイト (LeadByte), 第2バイト (TrailByte),区番号,点番号の範囲は次のとおり.
| IANA登録名 (別名) |
Shift_JIS (MS_Kanji,csShiftJIS) |
Windows-31J (csWindows31J) |
|
|---|---|---|---|
| 別名 (非IANA) | SJIS | CP932 | |
| 位置づけ | 標準版 | ベンダ独自拡張版 | |
| 2バイト文字 | 文字集合 | JIS X 0208 | JIS X 0208 ベンダ拡張文字 ユーザ定義外字 |
| 区番号 | 1〜94 | 1〜120 | |
| 点番号 | 1〜94 | ||
| LeadByte | 0x81〜0x9F,0xE0〜0xEF | 0x81〜0x9F,0xE0〜0xFC | |
| TrailByte | 0x40〜0x7E,0x80〜0xFC | ||
| 1バイト文字 (JIS X 0201) |
制御文字 (ASCII 互換):0x00〜0x1F,0x7F ラテン文字用図形文字 (ASCII とほぼ互換):0x20〜0x7E 片仮名用図形文字 (いわゆる半角カナ):0xA1〜0xDF 予約:0x80,0xA0,0xFD〜0xFF 未定義:0xF0〜0xFC (Shift_JIS の場合) |
||
TrailByte が取りうる値は 188 (=94×2) 通りで, これは点番号の範囲の丁度2倍になるように設計されたものである. つまり一つの LeadByte の値に対して,2区分のコードポイント範囲が対応する. したがって LeadByte の取りうる値の範囲は,区番号の範囲の半分 (Shift_JIS では 94/2=47 通り,Windows-31J では 120/2=60 通り) である.
シフト JIS の2バイト文字と区点番号は次のようにして対応付けられる.
なお JIS X 0208 の文字については,区番号および点番号それぞれに 0x20 を加算すると JIS コード,0xA0 を加算すると EUC-JP になる.
参考:シフトJIS / JIS X 0208 文字コード表 (現在の表示環境におけるシフトJISの2バイト文字集合を表示)
/*───────────────────────────────────── 入力 :byte:0x00〜0xFF. 戻り値:byte がシフト JIS 2バイト文字の第1バイトのときそのときに限り真. 2007/10/28(日) Shift_JIS (1〜94区) 限定版を追加. 2008/06/24(火) 改定 2010/03/18(木) 高速版の不等号の右辺の意味がわかりやすいように定数の表記変更. ─────────────────────────────────────*/ // 定義どおりの素直な判定方法. // (わかりやすいが,範囲の判定方法に無駄があるのであまり賢くない.) // Shift_JIS (1〜94区) 限定版 #define IsSjisLeadByte(byte) \ (ValueInRange(0x81U, (unsigned)(byte), 0x9FU) || \ ValueInRange(0xE0U, (unsigned)(byte), 0xEFU)) // ベンダ拡張版 (1〜120区) 用 #define IsSjisLeadByteX(byte) \ (ValueInRange(0x81U, (unsigned)(byte), 0x9FU) || \ ValueInRange(0xE0U, (unsigned)(byte), 0xFCU)) // ┌ここで見つけた巧妙な判定方法. // ↓(上の方法に比べ,条件分岐が2〜4回から1回に減るので少し高速化できそう.) // http://www.st.rim.or.jp/~phinloda/cqa/cqa15.html#Q4 // Shift_JIS (1〜94区) 限定版 #define IsSjisLeadByte(byte) \ ((((unsigned)(byte) ^ 0x20U) - 0xA1U) < (unsigned)(94/2)) // ベンダ拡張版 (1〜120区) 用 #define IsSjisLeadByteX(byte) \ ((((unsigned)(byte) ^ 0x20U) - 0xA1U) < (unsigned)(120/2)) // 2010/03/05(金) 追記 // この方法は LeadByte を0から始まる連続領域に移動 (「逆」シフト) させるので, // SJIS ⇒ 区点/JIS/EUC 変換に応用すれば判定と変換 (の一部) をまとめて行える. /*───────────────────────────────────── 入力 :byte:0x00〜0xFF. 戻り値:byte がシフト JIS 2バイト文字の第2バイトのときそのときに限り真. 2008/06/24(火) 改定 2010/03/03(水) 条件分岐を1回削減したバージョンを追加. 2010/03/18(木) 高速版の不等号の右辺の意味がわかりやすいように定数の表記変更. ─────────────────────────────────────*/ // 定義どおりの (以下同文) #define IsSjisTrailByte(byte) \ (ValueInRange(0x40U, (unsigned)(byte), 0xFCU) && ((unsigned)(byte) != 0x7FU)) // 上の巧妙な方法に倣って,条件分岐を1回削減したバージョン. #define IsSjisTrailByte(byte) \ ((((unsigned)(byte) - 0x40U) < (unsigned)(94 * 2 + 1)) && \ ((unsigned)(byte) != 0x7FU))
書きかけです.
準備中
dwFlags=MB_ERR_INALID_CHARS の場合
dwFlags=0 の場合
全般的なまとめについては準備中. 個別の2バイト文字の変換結果については, 「Windows-31J (CP932) 文字コード表」 (Windows-31J のすべての2バイト文字を MultiByteToWideChar() で変換したファイル (UTF-8/16BE)) を参照.
実験準備中.
対象文字コード:
とりあえず関連リンク.
Unicode 1文字を16ビットで表す. ただし第1〜16面 (U+010000 〜 U+10FFFF) の文字にはサロゲート・ペアを使用する.
サロゲート・ペアは本来 UTF-16 のものであり,それ以外では (UTF-16 をベースにした) UTF-7,そして残念ながら混乱の元となる CESU-8 でのみ用いられる (はず).
サロゲート・ペア内での UTF-16 符号単位の順序は (上位,下位) の順.
MySQL の UTF-8 も3バイト文字までしか対応していないらしい. つまり事実上 CESU-8 である.
| Unicode スカラ値 | 第1バイト | 第2バイト | 第3バイト | 第4バイト |
|---|---|---|---|---|
|
UTF8-1 0面0区0〜127点 (ASCII) U+0000 〜 U+007F (0000 0000 0xxx xxxx) |
0xxx xxxx (0x00〜0x7F) |
- | - | - |
|
UTF8-2 0面0区128点〜7区 U+0080 〜 U+07FF (0000 0xxx xxyy yyyy) |
110x xxxx (0xC2〜0xDF) |
10yy yyyy (0x80〜0xBF) |
- | - |
|
UTF8-3 0面8〜255区 U+0800 〜 U+D7FF U+E000 〜 U+FFFF (xxxx yyyy yyzz zzzz) Surrogate 領域 (U+D800 〜 U+DFFF) は使用禁止 |
1110 xxxx (0xE0) |
10yy yyyy (0xA0〜0xBF) |
10zz zzzz (0x80〜0xBF) |
- |
| 1110 xxxx (0xE1〜0xEC) | 10yy yyyy (0x80〜0xBF) |
|||
| 1110 xxxx (0xED) | 10yy yyyy (0x80〜0x9F) |
|||
| 1110 xxxx (0xEE〜0xEF) |
10yy yyyy (0x80〜0xBF) |
|||
|
UTF8-4 1〜16面 U+010000 〜 U+10FFFF (x xxyy yyyy zzzz zzww wwww) |
1111 0xxx (0xF0) |
10yy yyyy (0x90〜0xBF) |
10zz zzzz (0x80〜0xBF) |
10ww wwww (0x80〜0xBF) |
| 1111 0xxx (0xF1〜0xF3) |
10yy yyyy (0x80〜0xBF) |
|||
| 1111 0xxx (0xF4) |
10yy yyyy (0x80〜0x8F) |
RFC2279 は廃止されたが,次の理由でここに掲載する.
| UCS-4 | 第1バイト | 第2バイト | 第3バイト | 第4バイト | 第5バイト | 第6バイト |
|---|---|---|---|---|---|---|
|
0群0面0区0〜127点 (ASCII) 0000 0000 0xxx xxxx (U+0000 〜 U+007F) |
0xxx xxxx (0x00〜0x7F) |
- | - | - | - | - |
|
0群0面0区128点〜7区 0000 0xxx xxyy yyyy (U+0080 〜 U+07FF) |
110x xxxx (0xC0〜0xDF) |
10yy yyyy (0x80〜0xBF) |
- | - | - | - |
|
0群0面8〜255区 xxxx yyyy yyzz zzzz (U+0800 〜 U+FFFF) |
1110 xxxx (0xE0〜0xEF) |
10yy yyyy (0x80〜0xBF) |
10zz zzzz (0x80〜0xBF) |
- | - | - |
|
0群1〜31面 0000 0000 000x xxyy yyyy zzzz zzww wwww |
1111 0xxx (0xF0〜0xF7) |
10yy yyyy (0x80〜0xBF) |
10zz zzzz (0x80〜0xBF) |
10ww wwww (0x80〜0xBF) |
- | - |
| 0群32面〜3群 0000 00xx yyyy yyzz zzzz wwww wwvv vvvv |
1111 10xx (0xF8〜0xFB) |
10yy yyyy (0x80〜0xBF) |
10zz zzzz (0x80〜0xBF) |
10ww wwww (0x80〜0xBF) |
10vv vvvv (0x80〜0xBF) |
- |
| 4群〜127群 0xyy yyyy zzzz zzww wwww vvvv vvuu uuuu |
1111 110x (0xFC〜0xFD) |
10yy yyyy (0x80〜0xBF) |
10zz zzzz (0x80〜0xBF) |
10ww wwww (0x80〜0xBF) |
10vv vvvv (0x80〜0xBF) |
10uu uuuu (0x80〜0xBF) |
実験中なので,仕様は予告なく変更する可能性があります.
#include <assert.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <wchar.h> #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) // C99 以後 #include <stdint.h> #elif defined(__unix__) #include <sys/types.h> #else // 処理系に応じて変更すること. typedef unsigned char uint8_t; // 8ビット無符号整数 typedef unsigned short uint16_t; // 16ビット無符号整数 typedef unsigned int uint32_t; // 32ビット無符号整数 #endif typedef uint8_t utf8_t; // UTF-8 の符号単位 typedef uint16_t utf16_t; // UTF-16 の符号単位 typedef uint32_t utf32_t; // UTF-32 の符号単位 typedef uint32_t unicode_t; // Unicode スカラ値 typedef uint32_t ucs4_t; // UCS-4 の1文字 typedef int Bool; #define OK 0 #define FAIL (-1) // Unicode スカラ値の範囲 #define UNICODE_MIN ((unicode_t)0x000000) #define UNICODE_MAX ((unicode_t)0x10FFFF) // 無効な Unicode スカラ値 // (注意:規格とは無関係に,このページで独自に定義した値.) #define UNICODE_EOF (~(unicode_t)0) // 1面の最初のコードポイント #define UNICODE_PLANE1_MIN ((unicode_t)0x010000) // UTF-16 符号単位の範囲 #define UTF16_MIN ((utf16_t)UNICODE_MIN) #define UTF16_MAX ((utf16_t)0xFFFF) // UCS-4 の範囲 #define UCS4_MIN ((ucs4_t)0x00000000) #define UCS4_MAX ((ucs4_t)0x7FFFFFFF) /*───────────────────────────────────── 説明 :サロゲート領域の定義. 2007/08/12(日) 作成 2007/10/20(土) SURROGATE_BITS,{HIGH,LOW,BOTH}_SURROGATE_MASK を追加. ─────────────────────────────────────*/ #define HIGH_SURROGATE_MIN ((utf16_t)0xD800) // 上位サロゲート領域開始位置 #define HIGH_SURROGATE_MAX ((utf16_t)0xDBFF) // 上位サロゲート領域終了位置 #define LOW_SURROGATE_MIN ((utf16_t)0xDC00) // 下位サロゲート領域開始位置 #define LOW_SURROGATE_MAX ((utf16_t)0xDFFF) // 下位サロゲート領域終了位置 #define SURROGATE_MIN HIGH_SURROGATE_MIN // サロゲート領域開始位置 #define SURROGATE_MAX LOW_SURROGATE_MAX // サロゲート領域終了位置 // サロゲート可変部分のビット数 (上位,下位共通) #define SURROGATE_BITS 10 // サロゲート符号単位の可変部分を抽出するためのマスク #define HIGH_SURROGATE_MASK (((utf16_t)1 << SURROGATE_BITS) - 1) // 上位用 #define LOW_SURROGATE_MASK (((utf16_t)1 << SURROGATE_BITS) - 1) // 下位用 #define BOTH_SURROGATE_MASK (((utf16_t)1 << (SURROGATE_BITS + 1)) - 1) // 上下用 /*───────────────────────────────────── 機能 :有効なスカラ値か否かを判定する. 入力 :unicode:スカラ値. 戻り値:unicode が有効なスカラ値のときそのときに限り真. 2009/01/23(金) 作成 ─────────────────────────────────────*/ #define IsValidUnicode(unicode) \ (((unicode) <= UNICODE_MAX) && !IsSurrogate(unicode)) /*───────────────────────────────────── 機能 :UTF-16 の符号単位が上位サロゲートか否かを判定する. 入力 :utf16:UTF-16 の符号単位. 戻り値:utf16 が上位サロゲートのときそのときに限り真. 2007/08/12(日) 作成 2007/10/20(土) 改定 ─────────────────────────────────────*/ #define IsHighSurrogate(utf16) \ (((unicode_t)(utf16) & ~(unicode_t)HIGH_SURROGATE_MASK) \ == (unicode_t)HIGH_SURROGATE_MIN) /*───────────────────────────────────── 機能 :UTF-16 の符号単位が下位サロゲートか否かを判定する. 入力 :utf16:UTF-16 の符号単位. 戻り値:utf16 が下位サロゲートのときそのときに限り真. 2007/08/12(日) 作成 2007/10/20(土) 改定 ─────────────────────────────────────*/ #define IsLowSurrogate(utf16) \ (((unicode_t)(utf16) & ~(unicode_t)LOW_SURROGATE_MASK) \ == (unicode_t)LOW_SURROGATE_MIN) /*───────────────────────────────────── 機能 :UTF-16 の符号単位が上位または下位サロゲートか否かを判定する. 入力 :utf16:UTF-16 の符号単位. 戻り値:utf16 が上位または下位サロゲートのときそのときに限り真. 2007/08/12(日) 作成 2007/10/20(土) 改定 ─────────────────────────────────────*/ #define IsSurrogate(utf16) \ (((unicode_t)(utf16) & ~(unicode_t)BOTH_SURROGATE_MASK) \ == (unicode_t)SURROGATE_MIN) /*───────────────────────────────────── 機能 :UTF-16 の2つの符号単位の組 (first,second) がサロゲート・ペアか否か を判定する. 入力 :first,second:UTF-16 の符号単位.first が先,second が後. 戻り値:(first,second) がサロゲート・ペアのときそのときに限り真. 2007/08/12(日) 作成 ─────────────────────────────────────*/ #define IsSurrogatePair(first, second) \ (IsHighSurrogate(first) && IsLowSurrogate(second)) /*───────────────────────────────────── 機能 :IsHighSurrogate(),IsLowSurrogate(),IsSurrogate() のテスト. すべての UTF-16 符号単位についてこれらの述語の判定結果が正しいことを 確認するとともに,真理値表を out に出力する. 2007/08/12(日) 作成 2007/08/16(木) 改名 (SurrogateTest() ⇒ IsSurrogateTest()) 2007/10/20(土) 改定 ─────────────────────────────────────*/ void IsSurrogateTest(FILE *out) { unicode_t c; Bool isHighSurrogate, isLowSurrogate, isSurrogate; fprintf(out, "UTF-16\tHigh\tLow\tSurrogate\n"); for(c = UTF16_MIN; c <= UTF16_MAX; c++) { // Is{High,Low,}Surrogate() と別の方法で判定する. isHighSurrogate = ValueInRange(HIGH_SURROGATE_MIN, c, HIGH_SURROGATE_MAX); isLowSurrogate = ValueInRange(LOW_SURROGATE_MIN, c, LOW_SURROGATE_MAX); isSurrogate = ValueInRange(SURROGATE_MIN, c, SURROGATE_MAX); // 異なる判定方法の結果が一致することを確認する. assert(IsHighSurrogate(c) == isHighSurrogate); assert(IsLowSurrogate(c) == isLowSurrogate); assert(IsSurrogate(c) == isSurrogate); // 判定結果 (真理値表) を出力する. fprintf(out, "%04X\t%s\t%s\t%s\n", c, isHighSurrogate ? "*" : "", isLowSurrogate ? "*" : "", isSurrogate ? "*" : ""); } } /*───────────────────────────────────── テスト用 ─────────────────────────────────────*/ int main(void) { IsSurrogateTest(stdout); return EXIT_SUCCESS; }
/*───────────────────────────────────── 機能 :サロゲート・ペアを解読 (Unicode スカラ値に変換) する. 入力 :(1) high:上位サロゲート符号単位 (0xD800 〜 0xDBFF). (2) low :下位サロゲート符号単位 (0xDC00 〜 0xDFFF). 戻り値:サロゲート・ペアに対応する Unicode スカラ値 (U+010000 〜 U+10FFFF). 2007/08/12(日) 作成 2007/10/20(土) 改定 2008/06/24(火) 改定 ─────────────────────────────────────*/ unicode_t DecodeSurrogatePair(unicode_t high, unicode_t low) { return ((high & (unicode_t)HIGH_SURROGATE_MASK) << SURROGATE_BITS) + (low & (unicode_t)LOW_SURROGATE_MASK) + UNICODE_PLANE1_MIN; } /*───────────────────────────────────── 機能 :Unicode スカラ値 (1〜16面) を上位サロゲートに変換する. 入力 :unicode:Unicode スカラ値.1〜16面 (U+010000 〜 U+10FFFF) でなければ ならない. 戻り値:unicode に対応する上位サロゲート. 2007/08/16(木) 作成 2007/10/20(土) 改定 2008/06/24(火) 改定 ─────────────────────────────────────*/ #define Unicode_ToHighSurrogate(unicode) \ ((utf16_t)(((((unicode) - UNICODE_PLANE1_MIN) >> SURROGATE_BITS) \ & HIGH_SURROGATE_MASK) | HIGH_SURROGATE_MIN)) /*───────────────────────────────────── 機能 :Unicode スカラ値 (1〜16面) を下位サロゲートに変換する. 入力 :unicode:Unicode スカラ値.1〜16面 (U+010000 〜 U+10FFFF) でなければ ならない. 戻り値:unicode に対応する下位サロゲート. 2007/08/16(木) 作成 2007/08/28(火) 改定 (無駄を省いて簡略化) 2007/10/20(土) 改定 ─────────────────────────────────────*/ #define Unicode_ToLowSurrogate(unicode) \ ((utf16_t)((unicode) & LOW_SURROGATE_MASK | LOW_SURROGATE_MIN)) /*───────────────────────────────────── 機能 :Unicode_ToHighSurrogate(),Unicode_ToLowSurrogate(), DecodeSurrogatePair() のテスト. ・すべてのサロゲート・ペアについて,DecodeSurrogatePair(), Unicode_To{High,Low}Surrogate() の結果が正しいことを確認する. ・サロゲート・ペアと Unicode スカラ値の対応表を out に出力する. 2007/08/12(日) 作成 (DecodeSurrogatePair() のテスト.) 2007/08/16(木) Unicode_To{High,Low}Surrogate() のテストを追加. 2007/08/18(土) 改名 (DecodeSurrogatePairTest() ⇒ SurrogatePairConvTest()) 2007/10/20(土) 改定 2008/06/24(火) 改定 ─────────────────────────────────────*/ void SurrogatePairConvTest(FILE *out) { unicode_t high, low; // サロゲート・ペア unicode_t high2, low2; // Unicode_To{High,Low}Surrogate() の結果. unicode_t value; // DecodeSurrogatePair() の変換結果 // 第1〜16面のコードポイントを順番に. unicode_t refval = UNICODE_PLANE1_MIN; fprintf(out, "High Low Unicode\n" "---- ---- --------\n"); for(high = HIGH_SURROGATE_MIN; high <= HIGH_SURROGATE_MAX; high++) { for(low = LOW_SURROGATE_MIN; low <= LOW_SURROGATE_MAX; low++, refval++) { assert(IsSurrogatePair(high, low)); value = DecodeSurrogatePair(high, low); high2 = Unicode_ToHighSurrogate(value); low2 = Unicode_ToLowSurrogate(value); fprintf(out, "%04X %04X U+%06lX\n", high, low, value); assert(value == refval); assert(high2 == high); assert(low2 == low); } } assert(refval == UNICODE_MAX + 1); } /*───────────────────────────────────── テスト用 ─────────────────────────────────────*/ int main(void) { SurrogatePairConvTest(stdout); return EXIT_SUCCESS; }
/*───────────────────────────────────── 機能 :Unicode スカラ値 ⇒ UTF-16 変換 (UTF-16 文字列バッファに1文字追記). Unicode スカラ値 unicode を UTF-16 文字列バッファに追記する. 入出力:*pWritePtr:UTF-16 文字列バッファの,次の書き込み位置を指すポインタ. 書き込み成功ならば,次の書き込み位置を指すように更新される. 入力 :(1) bufEnd:UTF-16 文字列バッファの直後のアドレス.つまり UTF-16 バ ッファ がN要素の配列 buf[N] ならば &buf[N].ただし UTF-16 文字 列を必ず NUL 終端したい場合は &buf[N-1] とする (NUL は自分で書き 込むこと). (2) unicode:Unicode スカラ値. 戻り値:UTF-16 バッファに書き込まれた UTF-16 符号単位の数 (1〜2). バッファの空き容量不足で書き込めなかった場合には0. unicode が範囲外 (>U+10FFFF) の場合は -1. 2007/08/16(木) 作成 2007/10/20(土) 改定 ─────────────────────────────────────*/ int UTF16_PutChar(utf16_t **pWritePtr, utf16_t *bufEnd, unicode_t unicode) { utf16_t *dest = *pWritePtr; // 書き込み位置 if(unicode <= UTF16_MAX) { // unicode が0面の場合:そのまま書き込む. if(dest >= bufEnd) goto BufferFull; *dest = (utf16_t)unicode; *pWritePtr = dest + 1; // 次の書き込み位置 return 1; } else if(unicode <= UNICODE_MAX) { // unicode が1〜16面の場合:サロゲート・ペアに変換して書き込む. if(dest + 1 >= bufEnd) goto BufferFull; dest[0] = Unicode_ToHighSurrogate(unicode); dest[1] = Unicode_ToLowSurrogate(unicode); *pWritePtr = dest + 2; // 次の書き込み位置 return 2; } else { // unicode が17面以上の場合:エラー. return -1; } BufferFull: return 0; } /*───────────────────────────────────── 機能 :UTF-16 ⇒ Unicode スカラ値変換 UTF-16 文字列から1文字読み取り,Unicode スカラ値に変換する. 文字列は NUL 終端でも,そうでなくてもよい. 入力 :stringEnd:UTF-16 文字列の終端を指定する. (1) 非 NUL 終端文字列の場合:文字列の直後 (最後の符号単位の次のアド レス) を指す. (2) NUL 終端文字列の場合:NULL. 入出力:*pReadPtr:UTF-16 文字列の読み出し位置を指すポインタ.1文字を読み 出した後,次の読み出し位置に更新される.ただし文字列が NUL 終端 (stringEnd=NULL) で,既に NUL を指している場合には更新されない. 戻り値:*pReadPtr が指す1文字.ただし, ・NUL 終端文字列で,*pReadPtr が終端 NUL を指している場合は NUL を 返し,読み出し位置は更新しない. ・非 NUL 終端文字列 (stringEnd≠NULL) で,既に終端に達している場合 (*pReadPtr≧stringEnd) は UNICODE_EOF を返し,読み出し位置は更新 しない. ・**pReadPtr が孤立した ((正しい) ペアでない) サロゲートの場合は, それをそのまま返す.読み出し位置は次の符号単位に進む. 2007/08/17(金) 作成 ─────────────────────────────────────*/ unicode_t UTF16_GetChar(const utf16_t **pReadPtr, const utf16_t *stringEnd) { const utf16_t *src = *pReadPtr; unicode_t uc, uc2; if(stringEnd == NULL) { // NUL 終端文字列の場合 if((uc = *src++) != 0) { if(IsHighSurrogate(uc)) { uc2 = *src; if(IsLowSurrogate(uc2)) { uc = DecodeSurrogatePair(uc, uc2); src++; } } *pReadPtr = src; } } else { // 非 NUL 終端文字列の場合 if(src >= stringEnd) { uc = UNICODE_EOF; // 文字列終端 } else { uc = *src++; if(IsHighSurrogate(uc) && (src < stringEnd)) { uc2 = *src; if(IsLowSurrogate(uc2)) { uc = DecodeSurrogatePair(uc, uc2); src++; } } *pReadPtr = src; } } return uc; } /*───────────────────────────────────── 機能 :UTF-16 文字列の長さ (文字数および符号単位数) を取得する. 入力 :(1) string:UTF-16 文字列.NUL 終端でも,そうでなくてもよい. (2) stringEnd:string の終端. ・非 NUL 終端文字列の場合:string の最後の符号単位の次のアドレス. ・NUL 終端文字列の場合:NULL. 出力 :*pNUnits:pNUnits≠NULL ならば,string の UTF-16 符号単位の個数を返す. 終端 NUL は含まない. 戻り値:string に含まれる文字数.ペアをなさないサロゲートも1文字と数える. (UTF16_GetChar() が返す文字 (終端 NUL を除く) の個数と一致.) 2007/08/18(土) 作成 ─────────────────────────────────────*/ size_t UTF16_Length(const utf16_t *string, const utf16_t *stringEnd, size_t *pNUnits) { const utf16_t *p = string; size_t nChars = 0; unicode_t uc; if(stringEnd == NULL) { // NUL 終端文字列の場合 while((uc = *p) != 0) { p++; nChars++; if(IsHighSurrogate(uc) && IsLowSurrogate(*p)) p++; } } else { // 非 NUL 終端文字列の場合 while(p < stringEnd) { nChars++; uc = *p++; if(IsHighSurrogate(uc) && (p < stringEnd) && IsLowSurrogate(*p)) p++; } } if(pNUnits != NULL) *pNUnits = (size_t)(p - string); return nChars; } /*───────────────────────────────────── 機能 :UTF16_GetChar(),UTF16_PutChar(),UTF16_Length() のテスト. エラー処理は手抜き. (1) UTF-16 文字列 string を NUL 終端文字列として buf[] にコピーする (Unicode スカラ値経由) とともに,文字数および UTF-16 符号単位数 を数える. (2) UTF-16 文字列 string を非 NUL 終端文字列として buf[] にコピーす る (Unicode スカラ値経由) とともに,文字数および UTF-16 符号単位 数を数える.また UTF-32 ファイル UTF-32.txt に書き出す. (3) buf[] を UTF-16 ファイル UTF-16.txt に書き出す. 2007/08/18(土) 作成 ─────────────────────────────────────*/ void UTF16_Test(void) { // Windows では wchar_t=UTF-16 なので OK だが,他の OS ではダメかも. static const utf16_t string[] = L"\xFEFF" // BOM L"\x24B6牛丼一筋80年♪ \xD842\xDFB7野家\n" // サロゲート・ペア ([土]+[口]) L"\x24B7森\x9DD7外\n" // Windows-31J に存在しない文字 // [區鳥] L"\x24B8草\x5F45剛\n" // Windows-31J 機種依存文字 // [弓剪] L"\x24B9\x9AD9知新聞\n" // Windows-31J 機種依存文字 // (はしご高) L"\x24BA虹は♂,\x873Aは♀.\n" // Windows-31J に存在しない文字 // [虫兒] L"\x2707\x21D6\x21D7\x21D8\x21D9\n"; // Unicode 記号 utf16_t buf[64]; const utf16_t *src = string; const utf16_t * const stringEnd = string + wcslen(string); utf16_t *dest = buf; utf16_t * const bufEnd = ArrayEnd(buf); unicode_t uc; size_t size, nChars, nUnits; int result; FILE *out; // NUL 終端文字列としてコピーしてみる. for(nChars = 0; ; nChars++) { uc = UTF16_GetChar(&src, NULL); result = UTF16_PutChar(&dest, bufEnd, uc); printf("%06X %d\n", uc, result); if((uc == 0) || (result <= 0)) break; } if(result > 0) { assert(wcscmp(string, buf) == 0); assert(nChars == UTF16_Length(string, NULL, &nUnits)); assert(nUnits == wcslen(string)); printf("%lu characters, %lu units\n", (unsigned long)nChars, (unsigned long)nUnits); } printf("\n"); // 非 NUL 終端文字列としてコピーしてみる. // UTF-32 のファイルも作ってみる. out = fopen("UTF-32.txt", "wb"); assert(out != NULL); src = string; dest = buf; nChars = 0; while((uc = UTF16_GetChar(&src, stringEnd)) != UNICODE_EOF) { result = UTF16_PutChar(&dest, bufEnd, uc); printf("%06X %d\n", uc, result); if(result <= 0) break; size = fwrite(&uc, 1, sizeof(uc), out); assert(size == sizeof(uc)); nChars++; } fclose(out); if(result > 0) { assert(memcmp(string, buf, sizeof(utf16_t) * (size_t)(dest - buf)) == 0); assert(nChars == UTF16_Length(string, stringEnd, &nUnits)); assert(nUnits == (size_t)(src - string)); printf("%lu characters, %lu units\n", (unsigned long)nChars, (unsigned long)nUnits); } // UTF-16 のファイルも作ってみる. out = fopen("UTF-16.txt", "wb"); assert(out != NULL); size = fwrite(buf, sizeof(buf[0]), (size_t)(dest - buf), out); assert(size == (size_t)(dest - buf)); fclose(out); } /*───────────────────────────────────── テスト用. ─────────────────────────────────────*/ int main(void) { UTF16_Test(); return EXIT_SUCCESS; }
// UTF-8/CESU-8 1文字の最大バイト数 #define UTF8_RFC3629_MAXBYTES 4 #define UTF8_RFC2279_MAXBYTES 6 #define UTF8_MAXBYTES UTF8_RFC3629_MAXBYTES #define CESU8_MAXBYTES 6 /*───────────────────────────────────── 入力 :n:1 〜 UTF8_RFC2279_MAXBYTES. 戻り値:UTF-8 のnバイト文字に含まれるスカラ値のビット数. 2008/12/28(日) 作成 ─────────────────────────────────────*/ #define UTF8_BITS(n) (((n) == 1) ? 7 : (n) * 5 + 1) /*───────────────────────────────────── 入力 :n:1 〜 UTF8_RFC2279_MAXBYTES. 戻り値:UTF-8 でnバイト文字として表現可能な最大の Unicode スカラ値. 注意 :n=RFC3629_MAXBYTES の場合,0x10FFFF (RFC3629 での最大値) ではなく, 0x1FFFFF (RFC2279 での最大値) を返す. 用途 :UTF-8 非最短形式の効率的判定. 2008/12/28(日) 作成 ─────────────────────────────────────*/ #define UTF8_MAX(n) ((unicode_t)(((unicode_t)1 << UTF8_BITS(n)) - 1U)) /*───────────────────────────────────── 入力 :byte:UTF-8 文字列内の1バイト (無符号整数型). 戻り値:byte が UTF-8 のnバイト文字 (UTF8-n) の先頭バイトのときそのときに 限り,IS_UTF8_<n>(byte) は真を返す. 注意 :非最短形式の先頭バイト値 (0xC0〜0xC1,0xF5〜0xF7) を排除していない. 余談 :IS_UTF8_[2-6]() については,ValueInRange(0xC0, (byte), 0xDF) などと するのに比べて条件分岐が1回少ないのでちょっと速い(はず). 2009/02/10(火) 作成 ─────────────────────────────────────*/ #define IS_UTF8_1(byte) ((byte) <= 0x7FU) #define IS_UTF8_2(byte) (((byte) & 0xE0U) == 0xC0U) #define IS_UTF8_3(byte) (((byte) & 0xF0U) == 0xE0U) #define IS_UTF8_4(byte) (((byte) & 0xF8U) == 0xF0U) #define IS_UTF8_5(byte) (((byte) & 0xFCU) == 0xF8U) // RFC2279 のみ #define IS_UTF8_6(byte) (((byte) & 0xFEU) == 0xFCU) // RFC2279 のみ /*───────────────────────────────────── 入力 :byte:UTF-8 文字列内の1バイト (無符号整数型). 戻り値:byte が UTF-8 の複数バイト文字の第2バイト以後 (UTF8-tail) のとき そのときに限り真. 2009/02/10(火) 作成 ─────────────────────────────────────*/ #define IS_UTF8_TAIL(byte) (((byte) & 0xC0U) == 0x80U) /*───────────────────────────────────── 入力 :byte:UTF-8 文字列内の1バイト (無符号整数型). 戻り値:byte が UTF-8 の先頭バイトのときそのときに限り真. 注意 :非最短形式の先頭バイト値 (0xC0〜0xC1,0xF5〜0xF7) を排除していない. 2009/02/10(火) 作成 2010/03/18(木) 高速化のため判定順序を入れ替え. ─────────────────────────────────────*/ #define IS_UTF8_RFC3629_HEAD(byte) \ (!IS_UTF8_TAIL(byte) && ((byte) <= 0xF7U)) #define IS_UTF8_RFC2279_HEAD(byte) \ (!IS_UTF8_TAIL(byte) && ((byte) <= 0xFDU)) #define IS_UTF8_HEAD(byte) IS_UTF8_RFC3629_HEAD(byte)
時々「UTF-16 終端文字」,「UTF-8 終端文字」,「SJIS
終端文字」などで検索して来る人がいるが,
文字コードの規格で終端文字というものが決まっているわけではない.
「終端文字として何を使うか」ではなく,
「終端文字というものを使う」ことさえ規定されていない.
テキストファイルに終端文字なんてないでしょ?
えっ,それも知らないの? そういう人は,
時々テキストファイルをバイナリエディタで開いて見る習慣をつけると文字コードや文字列の正体が理解しやすくなると思うよ.
(例外:大昔 (1980年代) の CP/M や DOS
時代のテキストファイルの一部は終端文字として EOF 文字 (CTRL-Z=0x1A) を使っていたけど,
これは文字コードではなく OS (CP/M) の制約に由来する仕様.)
0 (NUL) をメモリ内の文字列の終端文字として使用するのはC言語 (およびその派生言語) の仕様. 正確に言うと,C言語であっても,すべての文字列が NUL 終端とは限らない. 文字列の終端方法は API やライブラリの仕様なので, それぞれのマニュアルでちゃんと確認すること. 例えば Windows の文字コード変換 API である MultiByteToWideChar() や WideCharToMultiByte() などは,文字列は NUL 終端でもそうでなくても使用できる.一般論で「(エンコーディング名) 終端文字」なんて検索しても正解が見つかるわけがない.
■注意
■余談
■参考
ASCII の NUL のコードポイントは 0x00,DEL は 0x7F であるが, そうなっている理由には元々物理的必然性があった.
ASCII が制定された当時,デジタル記録媒体として紙テープが使われていた. 紙テープは,穴が空いている状態が '1' を,穴が空いていない状態が '0' を表す. したがって紙テープの未使用の (まだ文字が書き込まれていない) 部分は 0x00 である.これを空文字 (NUL) と定義した.
紙テープ上に記録された文字を削除したい場合には, 専用の器具を使ってすべてのビットに穴を空ける. つまり 0x7F であり,これを削除済 (DEL) と定義した.
NUL も DEL も, 紙テープ読み込み時には無視 (読み飛ば) すべき文字コードだったのである.
■参考
|
インターネット時代の文字コード posted with amazlet at 10.07.08 小林 竜生 共立出版 売り上げランキング: 498047 おすすめ度の平均: ![]() 文字コードと文字フォントとコード処理方法 |
文字コード処理に役立つ (と思われる) データファイルを随時掲載します. ただし,筆者の理解不足や誤解により誤ったデータがあるかもしれませんので, ご利用は自己責任で.
データ形式は主として CSV と,Cのプログラムですぐ利用できる (構造体) 配列のソースにする予定.
「Shift_JIS (または CP932,Windows-31J) コード表」などで検索してくる人が多いので追加.
現在の表示環境で表示可能なシフトJISの2バイト文字を区点番号表で示す (1バイト文字については JIS X 0201 を参照). 1〜120区を収録してあるので,Shift_JIS (標準的なシフトJIS) が使用する2バイト文字集合である JIS X 0208 (1〜94区) だけでなく, Shift_JIS のベンダ拡張版である Windows-31J (CP932),MacJapanese 等の文字も表示可能.
このファイル自身,シフトJISコードで記述しているので, JIS X 0208 以外の文字がどう表示されるかは, このファイルの表示環境に依存する.Windows 環境では Windows-31J が,Mac では MacJapanese が,UNIX 環境では (たぶん) JIS X 0208 の文字集合だけが表示されるだろう. (ファイル内の機種依存領域の説明は,Windows-31J および NEC PC-9800 のものである.MacJapanese については Wikipedia を参考に記入.)
「シフトJIS / JIS X 0208 文字コード表」というタイトルだが, シフトJISのコード値ではなく区点番号で表示している.理由は,
JIS第1水準漢字 (16〜47区) は読みごと,第2水準漢字 (48〜83区 (1983年以降の追加分を除く)) は部首ごとに区点番号表を分けている.
注意:このファイルを別の文字コードに変換する場合, JIS X 0208 で定義されている文字以外は変換条件に応じた文字だけが表示されたり, 文字化けしたりする可能性がある.
ShiftJisTable.txt ダウンロード (プレーンテキストファイル (シフトJIS),91KB)上記「シフトJIS / JIS X 0208 文字コード表」を, Windows API の MultiByteToWideChar() で Unicode に変換したテキストファイル. Unicode なので Windows-31J の機種依存文字も文字コード上は機種依存ではなくなっているが, 表示環境の Unicode フォントがこれらの字形を持っていなければ表示できない点に注意. また MultiByteToWideChar() は Windows-31J の未定義/予約部分を Unicode の私用領域 (Private Use Area) に変換する (詳細は調査中) ので,その表示についても機種依存となる.
Windows-31J の文字集合を調べるだけでなく,MultiByteToWideChar() が Windows-31J (特に未定義/予約部分) をどのように変換するかを調べるためにも使用できる.
Windows-31J.lzh ダウンロード (UTF-8 + UTF-16(BE),64KB)行き当たりばったりにブックマークした,文字コード関連のリンク集 (未整理,敬称略) です.
当サイトは多漢字字形処理の環境整備を目的としたさまざまな技術の紹介や議論の場の提供を最終目標としていますが、現状では個人的な研究紹介のためのサイトとなっています。
花園フォント(花園明朝)は自由かつ無償の大規模フリー漢字フォントです。 非漢字は含まれません。現在は明朝体フォント1ウェイトが提供されています。
最近、文字列検索ライブラリを作成して気づいたのだが、C++ の標準ライブラリ cctype で定義されているグローバルな文字分類関数は、はっきりいって使ってはいけない。 非常に遅いのである。(中略) 一方、従来の C のやり方だと、(中略) 実に 8 倍以上の開きがある。なぜこんなことになっているのだろうか。
本稿は,POSIX において定義されている文字コード変換関数である iconv の Citrus Project における実装について述べたものである.本稿では,Citrus iconv の実装についての詳細を述べ,本 iconv 実装の利点および欠点について議論する.
|
深沢 千尋 ラトルズ 売り上げランキング: 46194 おすすめ度の平均: ![]() 隠れた名著 まぁまぁ 類似所の中では大変読みやすい書籍 面白いです。 |
|
Unicode標準入門 posted with amazlet at 10.04.13 トニー グラハム 関口 正裕 翔泳社 売り上げランキング: 210985 おすすめ度の平均: ![]() ISO/IEC 10646 Localization、Internationalizationの虎の巻です |
|
プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ) posted with amazlet at 10.04.13 矢野 啓介 技術評論社 売り上げランキング: 1383 |
|
Unicode Standard, Version 5.0, The (5th Edition) posted with amazlet at 10.04.13 The Unicode Consortium Addison-Wesley Professional 売り上げランキング: 77034 |
|
CJKV日中韓越情報処理 posted with amazlet on 07.05.07 ケン ランディ Ken Lunde 小松 章 逆井 克己 オライリージャパン (2002/12) 売り上げランキング: 177876 おすすめ度の平均: ![]() 分厚い。 |
|
文字符号の歴史―欧米と日本編 posted with amazlet on 07.05.07 安岡 孝一 安岡 素子 共立出版 (2006/02) 売り上げランキング: 174787 |
|
JISハンドブック 2010-64 (情報基本) posted with amazlet at 10.07.24 日本規格協会 売り上げランキング: 1029093 おすすめ度の平均: ![]() 情報処理技術者試験 必携 |
文字コード関連規格 (JIS X 0201, 0202, 0208, 0209, 0212, 0213, 0221 (Unicode)) や用語集などを含む,情報処理関連 JIS 規格.(目次)
|
|
このページの主な更新は Blog でお知らせします.
|
Copyright © 2007-2011 noocyte, All rights reserved. E-mail: relipmoced (a) yahoo.co.jp (" (a) " を半角のアットマークに書き替えてください.) リンクはご自由に. 「noocyte のプログラミング研究室」トップページに戻る. |
|