Haskellでの文字コードの扱い方が分かってきた
encodeString は Haskellの内部エンコーディングの文字列を UTF-8 に変換する。
decodeString は UTF-8 の文字列をHaskellの内部エンコーディングに変換する。
http://blog.kfish.org/2007/10/survey-haskell-unicode-support.html
しかしUTF-8のコマンドプロンプト上に日本語を表示するためにはputStrLnを使うだけでいい。
Prelude> putStrLn "あいうえお" あいうえお
これが混乱の元な気がする。これはシェルの環境を引き継いでいる。
% echo $LANG ja_JP.UTF-8
LANGをリセットしてやってみる。
% export LANG=C
putStrLn " <stdin>: hWaitForInput: invalid argument (Invalid or incomplete multibyte or wide character)
入力途中に怒られてしまった。
hSetEncodingを使うらしいけれど、ハンドルごとに引数を取るのがちょっとアレ。
環境変数に左右されるぐらいなら、ByteStringからunpackでStringに変換した方が良さそう。
ネットワークやCGIなどのモジュールはByteStringを扱うものが多いが、pack、unpackを使う場合はソース中の日本語文字が壊れる。
Haskellの内部エンコーディング文字列は扱えないようだ。上記動作のおかげで内部の文字列もUTF-8として扱えているものだと錯覚していた。
Prelude Data.ByteString.Lazy.Char8> Prelude.putStrLn $ unpack $ pack "あいうえお" BDFHJ Prelude Data.ByteString.Lazy.Char8> Data.ByteString.Lazy.Char8.putStrLn $ pack "あいうえお" BDFHJ
Prelude Data.ByteString.Lazy.Char8 Codec.Binary.UTF8.String> Prelude.putStrLn $ decodeString $ unpack $ pack $ encodeString "あいうえお" あいうえお
フォームやファイルなど外から来る日本語の文字列は decodeStringを使うとHaskell内部エンコーディングの文字列になる。
packに渡す文字列をソースコード内に記述した場合は encodeString を使ってUTF-8文字列にする必要がある。
CGIのoutput関数やこの前のメール送信用のコードは pack を使っている。
なのでソースコード内の日本語文字列は出力前にencodeStringで変換する必要があるようだ。
ファイルを読み込んでそのままCGIで表示する場合は encodeString は要らないが、ソース中の文字列と連結するなどができない。
-- templateString は Data.ByteString.Lazy.Char8.readFile で読み込んで unpack した文字列だとして… -- 結果をUTF-8で。 -- packに渡せる。出力用。 ret1 = templateString ++ (encodeString "日本語") -- 結果を内部エンコーディングで。 -- 文字の計算ができる。ソース内の日本語と連結とかlengthで数えるとか。 ret2 = decodeString templateString ++ "日本語"
何がややこしいって、両方ともStringなので段々訳が分からなくなってくるところ。折角の型システムがあるのだから型を変えてほしい…。
# その他メモ
サーバで設定してしまうのは考え物だが、Apache+FastCGIを使っているなら
FastCgiConfig -initial-env LANG=ja_JP.UTF-8
を設定することでSystem.IO.readFileがUTF-8のファイルを読めるようになった。
UTF-8からJISへ変換する場合を詳しく書いてみた。
% cat convert.hs import qualified Data.ByteString.Lazy.Char8 as BS import Codec.Text.IConv (convert) import Codec.Binary.UTF8.String (encodeString) main = do innerStr <- getContents -- UTF-8文字列を読み込むためには環境変数設定が必要。自動でdecodeStringされているようなもの? let utf8Str = encodeString innerStr utf8ByteStr = BS.pack utf8Str jisByteStr = convert "UTF-8" "ISO-2022-JP" utf8ByteStr jisStr = BS.unpack jisByteStr putStrLn jisStr % echo $LANG ja_JP.UTF-8 % ./convert > jis.txt ああ
新規作成された jis.txt を開くと文字コードがちゃんと変換されていることが分かる。しかし再び上書きしてみるとUTF-8になっていた。
System.IOのputStrLnじゃダメな気がするが、エラーは出ない。うーむ。