東京大学大学院数理科学研究科 豊田英司
toyoda@ms.u-tokyo.ac.jpこの文章は当初、1997年6月13日に東京大学理学部数学科で 行われた「計算数学」の講義ノートとして書かれました。
HTML 2.0 の記述力では少し不便を感じたので、 現バージョンではやむなく <table>, <center> を使っています。 またテーブルで bgcolor 属性が使えると図の色が見えてよいです (いちおう色が見えなくても読めるようには書いたつもりですが)。
また本講義ノートには無数の嘘や誤解されかねない文章がある可能性があります。 おかしいと感じたら豊田あてご連絡ください。
文字の関 まだ越えやらぬ 旅人は 道の奥をば いかで知るべき
―「字源」という漢和辞典の序文から
計算機にとって万物は数である。 (数といってもその実態は範囲の決まった整数や有限精度の浮動小数点実数などで あるわけだが以下では整数と思われたい)。 計算機は文字を表示したり保存したりするが、 それは文字につけた番号をつかって「86番を表示しろ」などと やっているだけでふつうは文字を知っているわけではない。 (表示装置やキーボードなどは CPU と分けて考える)。
計算機が文字を使えるようになるには 文字と数の対応づけという約束が必要である。 計算機ごとに約束が違うとある文字だったものが別の文字になったりする。 これを文字化けという。
本家アメリカの UNIX ではこの約束 (ASCII) を 1 つ知っていればいいのだが、 残念ながら ASCII は諸国語を想定したものではないし、 この文書自体がそうであるようにわれわれは日本語を主に使用しており、 しかも同じ文字集合のはずなのに諸般の事情でエンコーディングが乱立 していることもあって話がややこしい。
本講ではこの問題について概説する。 まず日本で UNIX を使うにあたって不可欠な素養として、 ASCII から ISO 2022 系へと発展してきた主な国家規格・国際規格の系譜を概説する。 大型機でのみ使われている EBCDIC やパソコン界のみで行われている拡張のような変なものはおいておこう。 Unicode / ISO 10646 については後でふれる (つもりである)。
文字と数の対応といっても世の中にはいろいろあるが、 いちばん影響力が強いのがアメリカ国家規格の ASCII で UNIX も ASCII を中心にできあがっている。 ASCII が決まったころは 7 ビット単位の処理が主流であったので、 ASCII には 0 から 127 (16進数では 0 から 7F) の数がある。
UNIX で man ascii とすると以下のような表が得られる:
上\下位 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
0 | NUL | SOH | STX | ETX | EOT | ENQ | ACK | BEL | BS | HT | LF | VT | FF | CR | SO | SI |
1 | DLE | DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN | EM | SUB | ESC | FS | GS | RS | US |
2 | SP | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / |
3 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
4 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
5 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
6 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o |
7 | p | q | r | s | t | u | v | w | x | y | z | { | | | } | 〜 | DEL |
名称 | 英語 | 日本語 (通称) | 16進 | C 記法 | stty 記法 | プリンタや CRT 端末での標準的機能 |
NUL | null | ヌル | 00 | \0 | ^@ | 無動作 (C で文字列の終わりに使う) |
BEL | bell | ベル | 07 | \a | ^G | ベルを鳴らす |
BS | back space | 後退 | 08 | \b | ^H | カーソルを後退させる |
HT | horizontal tab | 水平タブ | 09 | \t | ^I | 次のタブ位置にカーソルを移動 |
LF (NL) | line feed (newline) | 改行 | 0A | \n | ^J | 次の行にカーソルを移動 or 用紙を1行進める |
FF | form feed | (改ページ) | 0C | \f | ^L | プリンタの用紙を排出 |
CR | carrige return | 復帰 (リターン) | 0D | \r | ^M | カーソルを行頭に復帰 |
SP | space | 間隔 (スペース) | 20 | (' ') | (' ') | 何も表示しないで間隔をあける |
DEL | delete | 削除 | FF | \177 | ^? | 1文字削除 |
ちなみに UNIX の使える機械では通常、 SP と DEL 以外の制御文字はキーボードで CTRL と他のキー (@, A, B, ..., Z, [, ^, ], _) を同時に押して入力することができる。 たとえば復帰は return(enter) キーを押さなくても CTRL-M で入力できる。 そして ASCII における名称よりは CTRL-M (^M C-M などと書くことが 多い) などの通称のほうがよく用いられる。
ASCII は英語以外のことをかけらも考えていないので、 欧州諸国から「£」「e アクサンテギュ」「A ウムラウト」 などを使いたいという欲求が出てきた。
しかし 7ビット 空間はすでに埋まってしまったので、 上図の緑のところ (ASCII の #$@{\}^`{|}~ ) を国ごとに勝手に使ってよいことにする、 という方法で切り抜けることにした。これが国際規格 ISO 646 である。
国 | 規格名称 | ||||||||||||
米国 | # | $ | @ | [ | \ | ] | ^ | ` | { | | | } | 〜 | US-ASCII, ANSI X3.4-1968 |
日本 | # | $ | @ | [ | ¥ | ] | ^ | ` | { | | | } |  ̄ | JIS X 0201 (JIS C 6220-1969) Roman |
イギリス | £ | $ | @ | [ | \ | ] | ^ | ` | { | | | } | 〜 | BS 4730 |
フランス | # | $ | a´ | ° | c, | § | ^ | μ | e´ | u` | e` | ¨ | NF Z 62-010-1982 |
ドイツ | # | $ | § | A" | O" | U" | ^ | ` | a" | o" | u" | β | DIN 66 003 |
スウェーデン | # | >o< | E´ | A" | O" | A° | U" | e´ | a" | o" | a° | u" | SEN 85 02 00 Annex C |
スペイン | # | $ | ・ | 逆! | N〜 | C, | 逆? | ` | ´ | n〜 | c, | ¨ | IBM Spanish |
まあ要するに所詮これはその場しのぎであって、 何も考えないで国際的に情報交換すると イギリスでは「#」が「£」に化けるなどの問題が発生する。 (余談: Pascal で { } のかわりに (* *) と書けるとか、 C で \ のかわりに ??/ と書けるとかはこの問題に対処しようとしたものらしい)
我が日本でも ISO 646 の日本版ということで JIS X 0201 が制定された。 これは「¥」を「\」の位置に、 overline「 ̄」を tilde「〜」の位置に 置いている点が ASCII と異なる。 ちなみに JIS X 0201 ではこの 7ビット 空間ひとつだけではなく、 カタカナのためのもう一つの文字集合 (後に半角カタカナという汚らわしい通称で呼ばれることになる) を規定しているが、 これについては ISO 646 の枠外なので日本の項でのべよう。
上記のように ISO 646 の規格が乱立したわけだが、 それはたかだか「$」記号と「eアクサンテギュ」と「A ウムラウト」を 使いたいといった程度のことで数は知れている。 それならまとめちゃえばわざわざとっかえひっかえしなくてすむ、 というようなわけで ISO 8859 という文字集合ができた。
ISO 8859 には ISO 8859/1 から ISO 8859/10 まであって、 いずれも ASCII に含まれていない文字が最大 96 個含まれている。 (なぜ 96 かは ISO 2022 のところで説明する)。 これらは
というようになっていて、西欧語で使う非 ASCII 文字はすべて ISO-8859/1 (Latin 1 ともいう) に含まれている。これで上記の程度の文字を使うのに ASCII とフランス規格とドイツ規格を とっかえひっかえ、ではなく、ASCII と ISO 8859/1 を使うことにすればよくなった。 1バイトが8ビットで、それをフルに使えるならば (ISO 2022 的いうと G0 に ASCII を G1 に ISO 8859/1 を指示し GL に G0 を、GR に G1 を呼び出す、となるのだが、 ISO 8859 はそもそもそう使うつもりで作ったらしいのでぶっちゃけていうと) ASCII のところはそのままにしておいて 使われていない 128〜255 (80〜FF) の空き地に Latin 1 を入れておけば 西欧人だけにとっては楽園が実現するわけである。
世界を動かしているのは欧米なのでこれは非常に受けがよい。 多くの UNIX も ASCII + ISO 8859/1 をサポートしている。 あなたの UNIX 機械で運がよければ man iso_8859_1 のようなコマンドで Latin 1 の説明を読むことができるであろう。
また余談だが HTML 2.0 (RFC 1866) を見ると HTML で使えるのは ASCII + ISO 8859/1 となっている。つい最近 HTML 2.0 International Extention (RFC2070) が できるまでは日本語を書くことは「できなかった」。
さてこれらを同時にちゃんと区別して使いたいというこれまた当然な欲求が出てくる。 そのため「文字をとっかえひっかえして使う」という手順が決まった。 国際規格 ISO 2022 である。
ISO 2022 の全貌を紹介するのは本稿には荷が重い。 詳細が知りたい向きは
このように ISO 2022 に完全対応するのは相当めんどくさいので、 実際には目的に応じて ISO 2022 の適切な部分集合を用いることが行われている。 後述する 日本語EUC や junet code はこのような部分集合と解釈することができる。
このように複数の文字集合を使うようになると、ASCII だけの平和な世界と 異なり、「文字集合たちを実際にどういうバイト並びと対応させるか」 という方式が問題になる。これをエンコーディングという (と思うのだが正式な用語はよくわからない。間違ってたらごめん。 JIS X 0208 には「符号化表現」と書いてある)。
このような流れの中で日本ではどうしてきたか。 この文章自体がそうであるように、日本語には非常にたくさんの文字がある。 まずはエンコーディングの前に文字集合について語ろう。
正式名称は 「7ビットおよび8ビットの情報交換用符号化漢字集合」。
JIS X 0201 には2つの顔 (文字集合) がある。 ひとつは ISO 646 の日本語版。 これがあるからこそ日本で ASCII 相当品が「使える」のである。 これについてはもう述べた。
もうひとつはカタカナだけを含むもので、 JIS X 0208 が使えるのがあたりまえとなった今となっては過去の遺物というべきものである。
上\下位 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
2 | (SP) | 。 | 「 | 」 | 、 | ・ | ヲ | ァ | ィ | ゥ | ェ | ォ | ャ | ュ | ョ | ッ |
3 | ー | ア | イ | ウ | エ | オ | カ | キ | ク | ケ | コ | サ | シ | ス | セ | ソ |
4 | タ | チ | ツ | テ | ト | ナ | ニ | ヌ | ネ | ノ | ハ | ヒ | フ | ヘ | ホ | マ |
5 | ミ | ム | メ | モ | ヤ | ユ | ヨ | ラ | リ | ル | レ | ロ | ワ | ン | ゛ | ゜ |
6 | 未 | 定 | 義 | |||||||||||||
7 | 未 | 定 | 義 | (DEL) |
なぜかよくわからないが濁点つき仮名はなく、濁点が独立して存在している。 黄色いところ (16進 60 ... 7F) は未定義である。 ちなみにこの隙間を利用してシフトJIS という邪悪なものが発生した。 (大昔の一部の BASIC マシンみたいにこの隙間に 濁点つきカタカナをいれておけばシフトJIS みたいな変なものが 発生しなくてよかったのにね....)
正式名称は 「7ビットおよび8ビットの2バイト情報交換用符号化漢字集合」。 この文書で表示されている文字もほとんどが JIS X 0208 に定められたものである。
なにぶん 2 バイト (94 × 94) はさすがにしんどいから一覧表はものの本 (プリンタやパソコンのマニュアルにあるのはちょっと違うので できれば避けるように)か 京大大計の安岡さんの作られた表 をみよう。 要するにこれに含まれているのは
JIS X 0208 には ASCII や JIS X 0201 と共通する文字が含まれている。 これらは多くの世界で幅が異なって表示されることが多い ($ と $ など) ので、「半角」「全角」と呼んで区別することが多いが、 規格上は文字の幅は決まっていないから「全角」などの名称は間違っている。
そもそもこれらは同じ文字なのだから異なる文字のように呼ぶのも 間違っているのだが、現実に UNIX を使っていく上では JIS X 0208 の文字をコマンドや e-mail アドレスに使えない、 などの注意もしなければいけないので区別しないわけにもいかない。 そこで普通「日本語文字」「2バイト文字」「補助コードセット」(日本語EUC の場合) などと呼ばれているが、これらもかならずしもよい呼び方ではない。
なお現在の JIS X 0208 は 1997 年に改正されたものであるが、 1983 年版 1978 年版 に比べ文字が追加されている(最後の追加は 1990 年に行われた)。 困ったことに 1983 年版と 1978 年版では文字が入れ替わっているところがある (「籠篭」問題)。
補助漢字という。 JIS 第3水準という俗称もあるが、そう呼ぶのはあまりお勧めしない。 とりあえずあまり使われていない。
これはいわゆる Unicode である。
上記のほかに 現在「第3水準・第4水準」と称するもの が開発中である。
いまではもう少なくなりつつあるが、 伝統的な電子メール配送系では 1 バイトのうち 7 ビットの部分。 そこで、junet コードと呼ばれる ISO-2022-JP ( RFC 1468 という文書で規定されている) は ISO 2022 の 7 ビット部分を使う。 これは主に e-mail やネットニュースで使われている。
junet コードのことを JIS コードと呼ぶことがあるが、 EUC も JIS X 0202 (ISO 2022 に相当する国内規格) にのっとっているし、 シフトJIS も JIS X 0208 の付属書に書いてあり (これは1997年版で登場した)、 さらにいうなら Unicode さえ JIS になっているのだから、 JIS コードというのは意味不明である。 正式なものとは思えないが 7ビット JIS という言い方があるが、 まあこれならば意味が通じないことはないだろう。
文字集合を切り替える際には G0 -- G3 の呼び出しを変えるのではなく、 以下のような指示シーケンスで G0 に各種文字集合を指示することで 切り替えている。
ESC $ @ | JIS X 0208 の 1978 年版 (2バイト) |
ESC $ B | JIS X 0208 の 1983 年版 (2バイト) |
ESC ( B | ASCII (1バイトラテン文字) |
ESC ( J | JIS X 0201 のラテン文字 (1バイト) |
junet コードの利点は、文字の入れ替えなどで微妙に異なる JIS X 0208 の 1978/1983 年版の区別や、ASCII と JIS X 0201 の 区別がつけられることである。 欠点としては、JIS X 0208 の部分とラテン文字の部分が同じ 7ビットなので、 ファイルの途中から読み始める場合などはいったんさかのぼって、 最後の指示シーケンスを探し出さないとどちらの状態なのか判別できない (これを stateful という) ことがあげられる。
たとえば「あ」という文字は JIS X 0208 では16進表示で 24 02 であるが、 この部分を誤って ASCII とみなしてしまうと「$A」ということになり、 全然意味が変わってしまう。 この種の文字化けをしたテキストは通常、多くの「$」という文字を含む。 これは JIS X 0208 において ひらがな が16進で 24 で始まる位置にあるからである。
文字化けの危険を減らすために、junet コードでは
日本語EUC のことをただ EUC と呼んではならない。 EUC は Extended Unix Code の略で、1バイトが8ビットであるとして ISO 2022 の GR 集合や SS2 などを活用して作られた 文字エンコーディングのことで、日本語用だけではなく韓国語版・中国語版 なども存在する。日本語EUCはこの日本語版である。 日本語EUCはAT&T 漢字コードとも呼ばれ、 日本語に対応している UNIX の多くは内部的には EUC を使っている。
日本語EUCでは指示シーケンスは暗黙に仮定されており、現れることはない。 そのかわり、G1 は MSB が立っていることにより区別される GR 領域に 呼び出されているものとし、G2 と G3 は呼び出しによって文字集合を切り替える。 具体的には
なお、まっとうな ISO 2022 ならば SS2 や SS3 の後のバイト列は MSB が 立っていてはいけない (GL になければならない) ように思えるが、 EUC では GR の対応する位置のバイトを用いる。 これは ASCII と混同しないためである。 (果たしてこれでも ISO 2022 に適合するのであろうか?)
EUC の利点は ASCII 以外の文字集合に対応する部分は すべて MSB が立っているので ASCII とは容易に区別されるため、 ASCII のことしか考えていないソフトでも動作する可能性が高くなることである。 欠点としては (特に junet コードから変換する場合に重要な問題となるが) ASCII と JIS X 0201 のラテン文字を同一視したり、 JIS X 0208 の古い版を新しい版と同一視したりすることで いくつかの文字が混乱を起こすことがあげられる。
MS-DOS や Macintosh で用いられているというだけで長年 ろくな定義のなかったシフト JIS だが、JIS X 0208-1997 付属書2 で ついにまともな定義が与えられるようになった。
シフト JIS は ASCII (EUC と同じく JIS X 0201 ラテン文字と同一視することが多い), JIS X 0208, ならびに JIS X 0201 カタカナの共存を図るために 作られたエンコーディングであることは EUC と類似しているが、 伝統的な JIS X 0201 の使い方と互換性をもたせるために、 ISO 2022 なんかお構いなしに複雑な変換 (シフトというゆえん) をして 狭い空間に JIS X 0208 の文字を押し込んでいる。
まず ASCII はふつうの場所に配置する。 次に JIS X 0201 のカタカナを GR というべき位置に入れる。 すると空いているところは JIS X 0201 のカタカナ文字集合の「未定義」領域と、 ISO 2022 では MSB の立った制御文字として使われていた 16進で 80 -- 9F の領域である。 シフト JIS はこの部分を JIS X 0208 の第1 バイトとし、 これでは 94 通りがおさまらないので 続く第2 バイトを 94 の 2倍の広さとることで 94x94 文字集合をおさめている。 この変換はちょっとややこしいし、書いても意味がないので割愛する。
シフトJISの欠点は junet コードと EUC の悪いところをあわせて余りあるもので、
唯一利点といいうるものはあえて言うと、あまりのお行儀の悪さに 漢字コード自動判別プログラムがほぼ判定を誤らないことがあげられる。 というわけで出来損ないの WWW ブラウザに読ませる HTML ファイルは シフトJIS で書くと (まっとうな解決ではないが) いちばん楽である。
本文には junet コードを使うということになっている。 したがって JIS X 0201 のカタカナ文字集合 (いわゆる半角カタカナ)を使うことはできない。 特に EUC やシフトJIS などの MSB に意味がある漢字コードを 送ってしまうと、伝統的な 7ビット しか通さない配送系を通ったときに MSB が脱落してしまい、復旧が困難になる。
ただしヘッダには特別の注意を要する。 Subject: などの unstructured (機械が解釈することのない) 部分は junet コードをそのまま用いてもよいが、 From:, Date: や To: などの structured (機械が解釈する) 部分は ASCII でいう @ や < などにあたるコードが出る可能性が あるので junet コードを使ってはいけない。 ここでも ASCII 以外の文字を使う場合は次に説明する MIME を使うべきである。
というわけで何も考えたくなければ「ヘッダは MIME」とすることを いちおうお勧めしておくが、 Subject: に関しては junet コードをそのまま使う 人もまだいるようで、どうするのがいいのかはあまり確定的ではない。
伝統的な 7ビットしか通さない電子メール配送系を使って、 画像・音声などの各種ファイルや ASCII 以外の文字を送るために 作られた拡張。
なお、これを用いた email やニュース記事は Mime-Version: ヘッダを 持っているのでそれとわかることになっている。
日本語のかかれたファイルのエンコーディングを変換したいという 需要はときおり発生する。 まず覚えてもらいたいのがフィルタ型の変換ツールである。
また mule, nemacs, jvim などの日本語対応のエディタでも ファイル書き出し時のエンコーディングを指定してセーブすることで 変換することができる。 mule または nemacs ならば C-x C-k f としてからエンコーディングを指定し、 jvim ならば :set jcode=E などとする。
日本語入力用のソフトがなくなっても junet コードならば ファイルを作ることは容易である。
cat > filename
というコマンドで cat に対する標準入力をファイルにすることができるが、
ここで junet コードの指示シーケンスや JIS X 0208 の文字の番号
(に対応する ASCII 文字と思えばよい) をそのまま打ち込んでしまえばよい。
たとえば「あ」は16進数で 24 22 であるが、これを ASCII とみなせば
「$ "」であるから、前後に指示シーケンスをつけて
ESC $ @ $ " ESC ( B
と入力すれば、これは junet コードである。
なお、最後の ASCII に戻す指示シーケンスを忘れると以後の
ASCII のつもりの文字がすべて化けるので注意が必要である。
ISO 2022 的な端末を使っているときに、ISO 2022 のファイルを途中から読んだり、 ISO 2022 に対応していないソフトで操作したりすると、 指示シーケンスや呼び出しが失われたりして文字化けが生じることがある。
たいていの端末にはリセット用のコマンドがあって正気な状態に戻すことが
できるのだが、いつでも使える方法として、前述の cat を用いる方法が使える。
まず単に cat
と打ち、以下のような制御コードを
入力して、次に普通の ASCII 文字を打ったときのエコーバックが
正常になるか確かめてみるとよい。。
Ctrl-中クリック に続き Do Full Reset でリセットすることができる。 (嘘かいてました。ごめんなさい)
SP をスペースというのは正しいが、空白というのはよくない。 UNIX 系の文化では「空白」を whitespace の訳語として用いるが、 これはスペース・タブ・復帰・改行・鉛直タブの総称である。 ちなみに日本の規格では SP は正式には「間隔」と呼ばれている。
また JIS X 0208 のスペースは名称を「和字間隔」といい、 SP とは異なる文字となっているが、間隔の長さは決まっている わけではないので「全角スペース」と呼ぶのは不適当である。
確かに「違う文字」なのだが、同じ形でも許容されるらしい。 だから VM21 以降の PC-9800 は JIS X 0201 には適合していることになる。
上\下位 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
0 | NUL | SOH | STX | ETX | HT | DEL | VT | FF | CR | SO | SI | |||||
1 | DLE | DC1 | DC2 | DC3 | BS | CAN | EM | |||||||||
2 | FS | LF | ETB | ESC | ENQ | ACK | BEL | |||||||||
3 | SYN | EOT | DC4 | NAK | SUB | |||||||||||
4 | SP | . | < | ( | + | | | ||||||||||
5 | & | ! | $ | * | ) | ; | ||||||||||
6 | - | / | , | % | _ | > | ? | |||||||||
7 | : | # | @ | ' | = | " | ||||||||||
8 | aア | bイ | cウ | dエ | eオ | fカ | gキ | hク | iケ | コ | サ | シ | ス | セ | ||
9 | ソ | jタ | kチ | lツ | mテ | nト | oナ | pニ | qヌ | rネ | ノ | ハ | ヒ | フ | ||
A | sヘ | tホ | uマ | vミ | wム | xメ | yモ | zヤ | ユ | ヨ | ラ | リ | ル | |||
B | レ | ロ | ワ | ン | ゛ | ゜ | ||||||||||
C | A | B | C | D | E | F | G | H | I | |||||||
D | J | K | L | M | N | O | P | Q | R | |||||||
E | S | T | U | V | W | X | Y | Z | ||||||||
F | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |