Awkによる日本語文字の処理(メモ)

【このページは、慶応大学湘南藤沢キャンパス(SFC)で「地球環境観測」という授業を 担当していたときその参考として書いたものなので、そのローカルな事情にふれたところがある。】

わたしがAwkで処理するデータはおもに数値だが、文字列も含んでいる。 その大部分はASCIIだが、地名などはいろいろな言語の文字コードが含まれていることがある。

日本で作られたデータファイルには日本語文字を含むものが多い。 日本の多くの人は、 日本語文字列のマッチングや置きかえを含む処理をしたいだろう。 そういう処理の参考になる例文がのっている本は、 伊藤(1992)など1990年代に出されたものがいくつかある。

しかし残念なことに、Awk処理系(インタプリタ)の多くは、 1文字が1バイトであることを想定して作られてきたので、 1文字を2バイトで表わす日本語文字(ここではいわゆる「全角」の漢字・かなをさす)の 処理が期待どおりにならないことがある。

たとえば、文字コードとして日本語EUCが使われているとし、 ファイルokehutaには「桶蓋」という文字が含まれているとしよう。 2005年10月現在、SFC CNSのSun (ccz02)ではGNU Awk version 3.0.3、 Linuxマシンccx00 では同じくversion 3.1.0が コマンド名gawkでもawkでも動くようになっていた (ccx00のバージョンはその後変更されたようである)が、 そこでは次のようなことが起こる。

% gawk '/崖/' okehuta
桶蓋

これは、処理系がデータを1バイトずつ見て、 「桶」の2バイトめと「蓋」の1バイトめからなるバイト列が「崖」に一致すると判断してしまったのである。 また、文字コードEUCでは起こらないが、シフトJISの場合には、 2バイト文字の2バイトめがASCII文字(いわゆる「半角英数字」)に一致してしまうという問題も起こる。

そこで、日本語文字を認識するようにGNU Awkの改造が行なわれた。 かつては「日本語化したGNU Awk」という意味でjgawkと呼ばれていた。 伊藤(1992)などの日本語データ処理の例文はそれを前提としていた。 その後、jgawkの後継として、GNU Awkのマルチバイト拡張というものが作られている。 たとえば、 わたしが2005年10月現在個人マシンで使っていたPlamo Linux version 4.0に含まれた gawkは、GNU Awk 3.0.6 + multi-byte extension 1.15である。

このマルチバイト拡張の処理系では、 言語環境(環境変数LANG)がなんであっても、 オプションを指定しないときは文字列は1バイトが1文字であると解釈する。 (日本語環境であっても日本語文字を解釈することをdefaultにするのを避けたのは、 AwkはUnix系OSのシステム管理的な機能にも使われる可能性があるからだろう。) オプション「--ctype=EUC」または「-W ctype=EUC」を指定すると 文字コードとしてEUCが仮定される。 EUCの代わりにSJISと指定すればシフトJISが仮定される。 比較的新しいバージョンでは文字コードとしてUTF8も指定できる。 バイト数ではなく文字数を数える関数 jlength, jindex, jsubstrなどが追加されている。 (名前はjgawkから引き継いだものなのでjはもともと日本語をさしていたらしい。)

【日本語以外の2バイト文字についての動作は確かめていないが、 たぶん、中国語GB2312と韓国語KSC5601のそれぞれのEUC表現はEUCとして処理できると思う。 ただし、EUCには、日本語の場合を含め、2バイトで表現しきれない文字を扱う 拡張表現があるが、それはAwkのマルチバイト拡張では対象にしていない。 1バイトカタカナ(いわゆる半角カタカナ)のデータは、 SJISとみなせば正しく処理されるので、 わたしはまずSJISで動作するAwkプログラムで2バイト(全角)文字に変換したあと 別のプログラム(nkfなど)でEUCにコード変換して使うことが多い。 (nkfの新しいバージョンでは「半角」から「全角」への変換も できるらしいが、未確認。)】

一方、GNU Awkの(2005年10月現在)最新版の3.1.5や、 ひとつ前の3.1.4(SFCの教室のLinuxマシンにはいっているのはこれ)は、 それ自体でマルチバイト文字に対応している。 ただし、--ctypeに相当するオプションがあるわけではなく、 言語環境(環境変数LANG、 またはLC_で始まる名前の複数の環境変数)によって判断する。 たとえば、SFCの教室のマシンで、LANGja_JP.eucJPの ときには、「桶蓋」の中に「崖」は検出されなかったが、 LANGCあるいはenにしたときは検出された。 また、マルチバイト用の関数は追加されず、length, indexなどの関数自体が、バイト数でなく文字数を数えるようになっている。 LANGと一致する1種類の日本語文字コードと7ビットで表現できる範囲のASCIIだけを処理するならば、これでほとんど文字コードを意識せずにすみ、便利と思う人が多いにちがいない。 (システム管理的なプログラムにはちょっと注意がいるが、事前にLANG=Cと設定して実行すればよい。)

しかし、わたしは、端末入出力の言語は日本語EUCにしておきながら、 シフトJISのデータ、中国語のデータ、タイ語(8ビット必要な1バイト文字)のデータなどを処理したいと思うこともあるので、 プログラムが出すメッセージの文字コードと別に 処理対象の文字コードが指定できないと困る。 これは、正しく国際化された処理系であれば、 LANGLC_ALLを変更せずに、 LC_CTYPELC_COLLATEに処理対象の言語を設定すれば できるはずだ、というご指摘をいただいた。 試してみて、うまくいけばその方法に切りかえるつもりである。 ただし当面急いでそういう処理をしたいときは、 多少古いバージョンのGNU Awkのマルチバイト拡張の処理系を使い続けようと思っている。

【なお、「ソフトウェアの日本語対応」というと、 メッセージを日本語にすることをさすことが多い。 最近では、 メッセージ部分を抜き出したファイルにすることにより多言語対応をしやすくする技術が普及し、 とくにGNUソフトウェアの最近更新されているものはほとんどgettextライブラリによってそれを実現している(gettextの機能はそれだけではないが)。 しかし、メッセージを日本語にすることと、 データ処理の過程で日本語文字を認識して扱うことは、別々のことなので、 一方に対応しているソフトウェアが他方にも対応していると思いこまず、 実際にその機能があるかどうか確かめることが必要である。】

文献


2005-10-20, 更新 2006-10-06, 2015-06-14
増田 耕一 (MASUDA Kooiti)
[Awkの教材の目次へ] [コンピュータ関係の教材の目次へ]