node.jsでUTF-8-MACを扱う

node.jsをMacで動かした際に、軽くハマったので書いておく。


UTF-8文字コードでファイルにアクセスしようとした場合、
MacOSは内部で勝手に変換して、UTF-8-MACとしてファイルにアクセスするので、
意識せずにファイルを操作できる。
一方で、ファイル名を取得しようとすると、UTF-8-MACで戻ってくる。
lsコマンドを打つとターミナルによっては化けたりする。
まあ、Macを使っている人には、当たり前の話。

UTF-8-MACで困ること

これは直接ファイルをいじってる際には余り困らないのだが、
特定のディレクトリ以下のファイル一覧を何がしかのDBに突っ込むという処理をする場合、
何も考えずにDBにファイル名を書き込むと、UTF-8-MACで書かれることになる。
しかし、DBはMacOSがやってくれるように、勝手にUTF-8UTF-8-MACに変換したりはしない。
なので、ウェブブラウザ等から入力された文字列を元にクエリを構築してDBに投げた場合、
文字コードのズレで見つかるはずのデータが見つからないという困ったことになる。

対策

そもそも、UTF-8-MACなんて形式でDBに格納しておくのが良くない。
なので、格納する前に一般的なUTF-8に変換しておくのが普通だろう。
node.jsならnode-iconvというライブラリがあるので、これを利用したい。
ただ、一つ問題がある。
UTF-8-MACは普通のiconvでは利用できない。
MacOSのためにAppleが手を加えたiconvが必要になる。


node-iconvには、iconvのソースが同梱されており、それをコンパイルして利用するようになっている。
しかし、それを使ってしまうとUTF-8-MACって何だよと怒られる。
なので、AppleによるUTF-8-MAC対応版を利用する。
UTF-8-MAC対応版は以下からDL可能。


http://www.opensource.apple.com/tarballs/libiconv/
(いくつか並んでいるが、数字が大きいのが最新)


以下のような手順で対応した。

# モジュールをインストール
npm install iconv

# モジュールのディレクトリに移動
cd node_modules/iconv

# インストール時にコンパイルされたモジュールを削除
make clean

# Apple版のiconvをDLして、ソースコードを取り出し、node-iconvのdepsディレクトリに置く
wget http://www.opensource.apple.com/tarballs/libiconv/libiconv-30.tar.gz
tar xvzf libiconv-30.tar.gz
mv libiconv-30/libiconv deps/

Makefileを以下の様に修正する。

--- Makefile.org	2011-07-23 16:22:06.000000000 +0900
+++ Makefile	2011-07-23 16:13:15.000000000 +0900
@@ -5,10 +5,10 @@
 endif
 
 ifeq ($(NODE_INCLUDE_PATH),)
-	NODE_INCLUDE_PATH = $(NODE_PATH)/include/node
+	NODE_INCLUDE_PATH = $(NODE_PATH)/../../include/node
 endif
 
-LIBICONV_DIR	=deps/libiconv-1.13.1
+LIBICONV_DIR	=deps/libiconv
 LIBICONV	=$(LIBICONV_DIR)/lib/.libs/libiconv.a

NODE_INCLUDE_PATHを書き換えているのは、naveで設定される環境変数NODE_PATHが、
Makefileで想定されているディレクトリ構成とズレているから。


後は、makeコマンドを実行すれば、Apple版のiconvを利用するnode-iconvモジュールが出来る。

使い方 (CoffeeScript)

{Iconv} = require 'iconv'
conv = new Iconv 'UTF-8-MAC', 'UTF-8'
filename = conv.convert(filename).toString 'utf-8'


まあ、node.jsに限らず、iconv系のモジュールをMacで使う時は、
似たようなことしなきゃいけないって話。