最終更新 2003-11-11

UCSとUTF

ユニコードで文書を作るとき,文字コードの方式が2種類,あるいはそれ以上あることに気が付かれるかもしれません。例えば,一つはUnicodeとあるのに対し,もう一つはUnicode (UTF-8)と表記されているかもしれません。この2つは,また,その違いは何でしょうか(前者はUTF-16の一形態なのですが…)。

UCS-2とUCS-4
UTF-16
UTF-8
UTF-7
UTF-32

UCS-2とUCS-4

ユニコードが採択されることになった多言語用の文字コードセット,ISO-10846-1は,16ビット(16桁の二進数)でそれぞれの文字を表します。それをUCS-2 (Universal Character Set coded in 2 octets,「2つのオクテットでコードされたユニバーサル文字セット」)と称します。オクテットとは文字長の単位としての8ビット(8桁の二進数)のことです。

ユニコードの特定の文字は,例えば,'A' の文字なら,U+0041と書いて表記します。UCS-2では,U+0000からU+FFFFの文字を用います(可能性を含む)。

また,さらに多くの文字を含められるように4個のオクテットを用いるものをUCS-4といいます。UCS-4では,UCS-2の文字に対応する文字,U+00000000からU+0000FFFFに加えて,U+00010000からU+7FFFFFFFの文字を含みます(Unicode 3.0では,具体的な文字は確定しておらず,提案の段階のようです)。

UTF (Unicode (or UCS) Transformation Format)とは,これらUCS-2または UCS-4として定義された文字をバイト列(しかし本質的にはビット列かな)としてどのように表現するかの方式を指し,複数の方式があります。

UTF-16

UTF-16は,ユニコード1文字を,常に16ビットで表す文字コードです。[RFC2781]

厳密にいえばUTF-16には3種類あり,それぞれUTF-16,UTF-16BEないしUTF-16LEと呼びます。BEはビッグエンディアン 'Big Endian', LEはリトルエンディアン 'Little Endian' の略です。UCS-2のU+0041の文字(すなわち 'A')を,UTF-16BEでは00 41という2バイトで, UTF-16LEでは41 00という2バイトで表します。

一般的にいえば,UTF-16BEはUCS-2の上位オクテットを先にする方式であり,UTF-16LEは下位オクテットを先にする方式です。

UTF-16では通常,文書の始めにByte Order Mark(BOMと略されます。「バイト順を表す印」というような意味です)を付けます。これはUCS-2でU+FEFF(UCS-4ならU+0000FEFF)で表される文字ですが,文書の最初の2バイトをBOMと考え,それがビッグエンディアンのFE FFなら以降をUTF-16BEのように解釈し,BOMがリトルエンディアンのFF FE以降をUTF-16LEのように解釈します。

UTF-16,UTF-16BE,UTF-16LEの違いを示すため,'ABC'という3文字からなる文書の構成を示すなら次のようになります。

UTF-16BE
00 41 00 42 00 43
UTF-16LE
41 00 42 00 43 00
UTF-16のビッグエンディアン
FE FF 00 41 00 42 00 43
UTF-16のリトルエンディアン
FF FE 41 00 42 00 43 00
UTF-16のBOMなし
00 41 00 42 00 43

UTF-16BEやUTF-16LEにはBOMは付きません。付いていれば、U+FEFF、すなわち ZWNBSP としての意味を持ちます。もし逆のBOMが付いていたら,ユニコードではU+FFFEという文字は定義されていませんから,誤りのある文書ということになります。

文字コードがUTF-16であるとされる文書で,最初の2バイトがBOMとして解釈できない(FE FFでもFF FEでもない)場合,RFC2781によれば,ビッグエンディアンとして解釈されることになっています。

WindowsでいうUnicodeテキストとは,リトルエンディアンのUTF-16です。ビッグエンディアンのUTF-16は,ワードパッドやWordで読もうとしても正しく読めません。ただしInternet ExplorerなどのブラウザでUnicode (Big Endian) として表示し閲覧することならできます。BOMのないものは,今のところ,UTF-16文書として認識されないようです。

UTF-16では,UCS-4にあってUCS-2にない文字のうち,U+10000からU+10FFFFの範囲を,(unicode consortiumのいうところの)サロゲートペアとして表します。すなわち,U+D800からU+DBFFまでの文字を上位サロゲート(high surrogate)とし,U+DC00からU+DFFFまでの文字を下位サロゲート(low surrogate)として,2つの組み合わせて表現します。そのため,UTF-16では,UCS-4のU+110000以降の文字を表現することができません。

UTF-16で,U+10000からU+10FFFFの文字を表す方法を次に示します。

  1. 0x10000だけ差し引く(これを U' で表すことにする)。U+10000は 0x000000に,U+10FFFFは 0xFFFFFになる。
  2. U' を20ビットの2進数として表現する。
  3. 上位サロゲートをW1,下位サロゲートをW2とすれば,以下の関係になる。すなわち,U'の上位10ビットの上に '110110' を添えて W1とし,下位10ビットの上に '110111' を添えて W2とする。
       U' = yyyyyyyyyyxxxxxxxxxx (UCS-4 = U から0x10000だけ差し引いた数)
       W1 = 110110yyyyyyyyyy     (上位サロゲート:D800..DBFFになる)
       W2 = 110111xxxxxxxxxx     (下位サロゲート:DC00..DFFFになる)
    

例えば,U+12345なら,D8 08 DF 45と符号化されます(UTF-16BEの場合)。UTF-16LEなら,08 D8 45 DFの4バイトです。

UTF-8

UCS-2ではASCII文字も含めたあらゆる文字が2バイトで表されます。そうするとASCIIコードとすらも互換性がないので,必ずしも使いやすくありません。そこで,ASCIIとの互換性を保つために作られたのが(これだけの理由ではないかもしれませんが)UTF-8(Unicode (or UCS) Transformation Format, 8-bit encoding form)です。[Unicode Standard 4.0, RFC3629, RFC2279 (obsolete) ならびに RFC2044 (obsolete)]

UCS-2ではASCII文字にはナル文字 00 が必ず付いていたのに対し,UTF-8ではその必要がありません。また,BOMもありません。UTF-8には FE または FF といったバイトが含まれることはありません。そのため,UTF-16のBOMと紛れることがありません。

ASCII文字が1バイトで表される代わり,ギリシア文字やロシア文字は2バイト,漢字や仮名などは3バイトになります。UCS-2 (UCS-4) とUTF-8の間には,次の関係があります(U+200000以降も方法は定義されていますが省略します)。

UCSからUTF-8への変換法
UCS-2 (UCS-4)ビットパターン 第1バイト第2バイト第3バイト第4バイト
U+0000 .. U+007F00000000-0xxxxxxx0xxxxxxx
U+0080 .. U+07FF00000xxx-xxyyyyyy110xxxxx10yyyyyy
U+0800 .. U+FFFFxxxxyyyy-yyzzzzzz1110xxxx10yyyyyy10zzzzzz
U+10000..U+1FFFFF00000000-000wwwxx-
xxxxyyyy-yyzzzzzzz
11110www10xxxxxx10yyyyyy10zzzzzz

ご覧の通り,UTF-8では,非ASCII文字にはMSB(最上位ビット)が立った(1になった)コード(二進数で10000000から11111111の文字)を用いるのでASCIIとは完全に区別されます。また,文字の順序は,UCS-2とUTF-8で変わりません。

1バイトの文字は[00-7F]であり,2バイトの文字は[C2-DF]+[80-BF]であり,3バイトの文字は[E0-EF]+[80-BF]+[80-BF]の組み合わせとなります。UTF-8の肝心な点は,1文字が何バイトであるが,先頭バイトを読みこんだ段階で明白に判るという点です。よって,UTF-8の文字列を処理する場合,先頭から順に読みこめば正確に処理できます。逆に,途中から抜き出した部分文字列は,必ずしも正しくは処理できません。

UTF-8として正当なバイト列は,次のようにまとめられます。UTF-16 では表現できない U+D800..U+DFFFの範囲ならびに U+110000以降の文字は不正です。

正当なUTF-8のバイト列
コードポイント 第1バイト 第2バイト 第3バイト 第4バイト
U+0000..U+007F 00..7F
U+0080..U+07FF C2..DF 80..BF
U+0800..U+0FFF E0 A0..BF 80..BF
U+1000..U+CFFF E1..EC 80..BF 80..BF
U+D000..U+D7FF ED 80..9F 80..BF
U+E000..U+FFFF EE..EF 80..BF 80..BF
U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
U+100000..U+10FFFF F4 80..8F 80..BF 80..BF

注意すべきは,C0 80E0 80 80のようなバイト列です。UCSからUTF-8への変換法で,UTF-8が2バイトまたは3バイトになる列と比べると,x,y,zのビットがすべて0になったときに,C0 80または E0 80 80となるように見えます。これらはUCS-2のU+0000と対応するように感じられるかもしれません。

                  UTF-8                       UCS-2           
            110xxxxx-10yyyyyy             00000xxx-xxyyyyyy   
C0 80    = 11000000-10000000          → 00000000-00000000 = U+0000 ?

            1110xxxx-10yyyyyy-10zzzzzz    xxxxyyyy-yyzzzzzz   
E0 80 80 = 11100000-10000000-10000000 → 00000000-00000000 = U+0000

しかし,こうしたバイト列は [RFC2279]によれば不正(illegal)であり,認められません。つまりUTF-8では,U+0000からU+007Fの文字は常に1バイトであり,U+0080からU+07FFの文字は常に2バイトでなくてはなりません。

Unicodeでは,往復変換(roundtrip)を保証すべきです。`UTF-8'のC0 80をUCS-2のU+0000に対応付けるようなプログラムでは,そのUCS-2を再びUTF-8に変換した時に00になってしまいます。このような事態は好ましくありません。ですから,C0 80のような文字列は「不正」とみなさなければなりません。

`UTF-8'のC0 80をUCS-2のU+0000に対応付けるようなプログラムでは,例えば,ある種の危険な文字列(命令文など)を除く処理をしたはずなのに,除かれなかったというような事態が起こりえます。ですから,上の表にある文字だけを正当なUTF-8文字と考える必要があります(cf. RFC2279, Security Considerations)。

なお,UTF-16でサロゲートペアとして表現する文字は,UTF-8では4バイトで表します。例えば,UCS-4のU+010000は,UTF-16BE ではD8 00 DC 00として表現されますが,これをUTF-8で表現する時は,F0 90 80 80としなければなりません。サロゲートペアを真似て,U+D800,U+DC00の2文字と考えて,ED A0 80 ED B0 80と6バイトで表すのはいけません。U+D800,U+DC00のようなサロゲートペアの範囲は、Unicode 3.2 以降、不正ですので使ってはいけません。

UTF-7

メールでユニコードを用いることができるように,UTF-7という,7ビットの符号化方式もあります。[RFC2152 ならびに RFC1642 (obsolete)]で述べられています。

UTF-7はBASE64を基礎にした符号化法で,文字列を3種の部分に分けます。

  1. Set D (directly encoded characters)。直接埋め込む文字:A-Z, a-z, 0-9および次の9文字です("+" と "=" は除外されます)。
    文字  '   (   )   ,   -   .   /   :   ? 
    10進  39  40  41  44  45  46  47  58  63
    
  2. Set O (optional direct characters)。 直接埋めこんでもよい文字:次の20文字です("\" と "~" は除外されます)。
    文字  !  "  #  $  %  &  *  ;  <  =  >  @  [  ]  ^  _   `   {   |   }
    10進  33 34 35 36 37 38 42 59 60 61 62 64 91 93 94 95 96 123 124 125
    
  3. Set B (Modified Base 64)。パッド文字 "=" はUTF-7では用いません。

なお,BASE64のアルファベットは以下の通りです。

            Table 1: The Base64 Alphabet [RFC2045]

     Value Encoding  Value Encoding  Value Encoding  Value Encoding
         0 A            17 R            34 i            51 z
         1 B            18 S            35 j            52 0
         2 C            19 T            36 k            53 1
         3 D            20 U            37 l            54 2
         4 E            21 V            38 m            55 3
         5 F            22 W            39 n            56 4
         6 G            23 X            40 o            57 5
         7 H            24 Y            41 p            58 6
         8 I            25 Z            42 q            59 7
         9 J            26 a            43 r            60 8
        10 K            27 b            44 s            61 9
        11 L            28 c            45 t            62 +
        12 M            29 d            46 u            63 /
        13 N            30 e            47 v
        14 O            31 f            48 w         (pad) =
        15 P            32 g            49 x
        16 Q            33 h            50 y

UTF-7を符号化するには次の手順によって行います。

  1. Set Dのユニコード文字を等価なASCII文字として符号化する。Set Oに属する文字は直接符号化してもよいが,メールヘッダの構成文字としては使えないものが多いことなど要注意である。
  2. その他の文字はBASE64で符号化する。Set D or Set OからSet Bへの切り替え時には "+" が入る。Set BはBASE64アルファベットでない文字の出現によって終わるが,間に終端文字として "-" を入れてもよい。Set B領域に続くSet D or Set O領域の最初の文字が "-" であるときは,かならず終端文字として余分な "-" を入れる。
  3. 終端文字は,引き続くSet D or Set O領域の最初文字がBASE64アルファベットの1つである時には必須である。
  4. "+" の文字自体は,"+-" という文字列で表す。
  5. Set Bに埋めこむUnicode文字列は,UTF-16としてからBASE64に符号化する。
  6. SPACE, TAB, LF, CRはASCIIとして埋めこんでもよい。

例:"日本語" という文字列は,U+65E5,U+672C,U+8A9Eだから,+ZeVnLIqe-と符号化する。

011001-011110-010101-100111-001011-001000-101010-011110
    25     32     21     39     11      8     42     30
     Z      e      V      n      L      I      q      e

例:U+263A = 001001-100011-1010 は,001001-100011-101000と後ろにビットを補ったうえで,+Jjo-と符号化する。

UTF-32

UTF-32は,ユニコード1文字を,常に32ビットで表す文字コードです。[Unicode Technical Report #19]

UTF-32とUCS-4の違いは,UTF-32ではUTF-16と同じく,U+0000からU+10FFFFまでの範囲しか有効ではないことです。つまり,UTF-32はUCS-4のサブセットといえます。

厳密にいえばUTF-32には3種類あり,それぞれUTF-32,UTF-32BEないしUTF-32LEと呼びます。こうくると予想がつくかもしれませんが,これらの違いはUTF-16のと同様です。

UTF-32ではUTF-16のようなサロゲートペアは用いません。U+10203の文字なら,UTF-32BEでは00 01 02 03と符号化され,UTF-32LEなら03 02 01 00として表現されます。

UTF-32ではBOMは,UCS-4のU+FEFFで表される文字です。文書の最初の4バイトをBOMと考え,それがビッグエンディアンの00 00 FE FFなら以降をUTF-32BEのように解釈し,BOMがリトルエンディアンのFF FE 00 00なら以降をUTF-32LEのように解釈します。BOMであれば,それは単にバイト順を表すためのものであって,文書を構成する文字とはみなしません。BOMでなければ,全体がビッグエンディアンであるとみなして解釈します。

UTF-32,UTF-32BE,UTF-32LEの違いを示すため,'ABC'という3文字からなる文書の構成を示すなら次のようになります。

UTF-32BE
00 00 00 41 00 00 00 42 00 00 00 43
UTF-32LE
41 00 00 00 42 00 00 00 43 00 00 00
UTF-32のビッグエンディアン
00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43
UTF-32のリトルエンディアン
FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00
UTF-32のBOMなし
00 00 00 41 00 00 00 42 00 00 00 43

戻る