Windows版HaskellとCygwinの文字コード
Windows版HaskellをCygwinから使っていて文字コードでトラブったので、その備忘録です。
きっかけは
main = putStrLn "ℕ"
が
a.hs: <stdout>: commitBuffer: invalid argument (invalid character)
というエラーを吐いたことです。"ℕ"じゃなくて"あ"なら出るんです。
ここで「なんだ、Haskellはユニコードをまともに扱えないのか」と思ってしまいました。ユニコード使いまくりの自作言語を作ってみたかったので「じゃあRubyでやるか」となったのですが、Raccでもℕが期待通りに動かなかったので、もう一度Haskellで試してみることにしました。
unicode-show
をcabalから入れて、uprint
を試すも、特に変化なし。
{-# LANGUAGE UnicodeSyntax #-}
を付けてみるも変化なし。
setLocaleEncoding utf8
を試すとエラーは出なくなるけど文字化けする。
ここで
import GHC.IO.Encoding
main = do
getLocaleEncoding >>= print
の結果がCP932
でした!!!
「えっ?なんで?だってCygwinのターミナルはutf-8のはずでは?だったら"あ"とかも出ないはずでは?」と思いぐぐってみると20190222: Cygwin - ターミナルエミュレータと文字化けというページがありました。
どうやら、conhostというのが勝手に変換しているようなのです!そして、パイプでつなぐと最後のコマンドの文字コードが変換の根拠になるもよう。ということで、
import GHC.IO.Encoding
main = do
setLocaleEncoding utf8
getLocaleEncoding >>= print
putStrLn "あいうえおℕ"
をrunghc utf-8.hs | cat
で実行したら正常に出た!!!
どうやら、最初の例でエラーになったのはcp932にℕが無いからのもよう。単純にutf-8.hsを実行するとconhostがプログラム中でエンコーディングを変更したのを認識せずにcp932だと思ってutf-8に変換するからのもよう。| cat
を付けるとCygwinのコマンドが最後に実行されたのでutf-8だと判断して素通しにするもよう。全部推測でしかありませんが…
これはCygwinがやっているのかとも思いましたが、cmdからWSLを使って日本語文字列をパイプで扱うとcmdのコードページが壊れるというページがあって、Cygwinと関係なく起こる事象のようです。どうやら、WSLの導入にあたってWSL側の文字コードとcmd側の文字コードの違いに悩まなくていいように自動変換が導入されたみたいですね。勝手にやるなよ…
Haskellのプログラム中でsetConsoleOutputCP 65001
の指定でも正常に動きました。setLocaleEncoding
はプログラム中で使うエンコードの変更で、SetConsoleOutput
が出力するエンコードの指定なのかと思ったけど、どうやら後者はchcp 65001
と同じ動作のようです。でも、なんでそれで動くのかよくわからない。
ちなみにCygwinからchcpを使うときはchcp.com 65001
みたいに.com
を付けて使うようです。DOSからだと.exe
と.com
は補ってくれるけど、Cygwinからは.exe
だけということでしょうか?
Haskellでimport System.Win32.Console
とかはhidden packageだから明示的に指定しないと使わせないというエラーを出すんですね。そこまでわかっているなら使わせてくれても良さそうだけど…
b.hs:5:1: error:
Could not load module eSystem.Win32.Consolef
It is a member of the hidden package eWin32-2.6.2.1f.
You can run e:set -package Win32f to expose it.
(Note: this unloads all the modules in the current scope.)
Use -v (or :set -v
in ghci) to see a list of the files searched for
そして、runghc -package Win32
とやってもエラーになる。どうやらrunghc --ghc-arg=-package=Win32 b.hs
とやらなければならないもよう。なんでこうなっているのだろう?
ぐぐって調べている途中で見つけたのですが、HaskellとPythonはユニコードのコードポイントを内部表現で使っていて、Javaとかは特定のエンコーディングが指定されているみたいですね。Rubyは変態でCSIというコードの情報を持つバイト列みたいです。ユニコードのコードポイントを使う方式もUCSの一つということになるのでしょうか?
Windowsのデフォルト文字コードをutf-8に変更することもできるみたいです。というか、まだなっていなかったのか。でも、なにがあるかわからないからこのままでいいか。
コメント
コメントを投稿