uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

groovyでスクリプトのエンコーディングを指定する

前に、「コメントに何を書いてもいい」と思うのは、/**/については正しいが、//については正しくない」という記事を書きましたが、そこでの対策との一つとしては、「スクリプトのエンコーディングを正しく指定する」でした。

で、続編として「Groovyでスクリプトのエンコーディングを設定する方法」について調べてみましたので書いてみます。嘘があったら済みません。コメント欄などでご指摘ください。

まず、対処の方向性としては2つ3つがあって、つまり

であります。なお、(1)をしてよいのであれば、(2)の設定は(1)を引き継ぎます(2)の設定は、SimpleTemplateやTemplateServlet(GSPみたいなもの)エンコーディング以外については、(1)を引き継ぐので、SimpleTemplate/TemplateServletを使わない場合は、(1)だけで十分です。使う場合(例えばGaelykを使う場合)は(1)(2)の両方を設定しておくと良いようです。

システムプロパティの設定方法としては、

  • (a)Java環境全体への設定として、_JAVA_OPTIONSという環境変数で-Dxxx=yyyのように設定する
  • (b)Groovyの設定として、JAVA_OPTSという環境変数に-Dxxx=yyyのように設定する
  • (c)GAEアプリの場合、appengine-web.xmlで設定する。

という2つ3つの方法があります。JAVA_OPTSを読み取っているのは<GROOVY_HOME>/bin/startGroovy(.bat)です。

ところで、今のこの日本において、Java環境のデフォルトエンコーディングを変えたくなるのは、デフォルトエンコーディングShift_JIS系のWindowsのJava環境とMacのJDK6以降であり、かつ、実際に変えたいのは、UTF8にしたいという話ですよね。と、決めつける。

まとめ

まとめに入ります。以下に対処案を示します。すべてBashの記法です。

案1

なるべく限定的に、Groovyスクリプトのソースコードの問題だけを対処したいなら、

export JAVA_OPTS='-Dgroovy.source.encoding=UTF-8'

とする。この場合、デフォルトエンコーディングに変更は無い。つまり、デフォルトエンコーディングがSJISである場合、上記を行えばUTF8で書かれたGroovyコードを文字化け無く読み取って実行できるが、この中で例えば(UTF8で)書かれた「pritln "ほげ"」とかいうコードで出力される文字列は、デフォルトエンコーディング(SJIS)に従うし、File#eachLine()で読み取る文字列もSJISであることが期待され、他の文字コードなら化ける。簡単な回避策は無い。

案2

SJISとは手を切りたく、ソースコードも出力もUTF-8にしたい場合で、ただしJava環境全体に影響は与えたくはなくて、Groovy限定で良い場合。

export JAVA_OPTS='-Dfile.encoding=UTF-8'

この環境変数はstartGroovyが読み取るものなので、Groovy以外には影響を与えません。とはいえ、他のツールで使っている環境変数名と偶然一致しているという可能性はありますね。

案3

Groovyに限定せず、Java全体に関してデフォルトエンコーディングを全てUTF-8に変更したい場合。根源的対策*1

export _JAVA_OPTIONS=-Dfile.encoding=UTF-8

しかし、この場合、1点問題があって、Macだけかもしれせんが、JVM起動のたびに1行よけいなエラーメッセージ「Picked up _JAVA_OPTIONS: ..」とかいうのが標準エラー出力に出てきます。はなはだしくウザく、回避策は不明です。

案4
alias groovy='groovy -Dfile.encoding=UTF-8'
alias java='java -Dfile.encoding=UTF-8'
alias javac='javac -J-Dfile.encoding=UTF-8'

とか。

なぜこんなことをぐだぐだとわざわざ書いてるのか?

Javaの場合、直接Javaコマンドでプログラムを起動することが少なく、バッチファイルやシェルスクリプトで起動するwrapperを経由することが多いので、エンコーディングはそれらのスクリプト中で明示的に-D=file.encoding=UTF8オプションがつければ良い話です(ついてなければスクリプトを書き換えてつける)。あるいはIDEの設定で済む話です。

しかし、Groovyの場合、書き捨ての小さなスクリプトなどを「頻繁に」かつ、「コマンドラインから」、「groovyコマンドから直接」実行する使い方をしたくなるときが多くあり、困るのですよ。

まとめ

ちなみに私としては案1+案2で

export JAVA_OPTS='-Dgroovy.source.encoding=UTF-8 -Dfile.encoding=UTF-8'

こんな風にしてます。file.encodingだけではなくgroovy.source.encodeingも指定しているのは何となく念のためです。(追記: 山本さんの指摘によれば、Gaelykなどではこの対処はビンゴ。テンプレートを扱う場合などは、両方を設定した方がいいときがあるようです。)

*1:ただし、以下は以下を実行したシェルとその子プロセスにしか影響を与えないので注意。GUIとかIDEから起動した場合にも効力を発揮したい場合は違う設定方法となるでしょう。