Edited at

Ruby で UTF-8 なのにマジックコメントが必要なケース


何の話?

Ruby のスクリプトは UTF-8 のほか CP9321 など複数の文字コードで書くことができる。

スクリプトを記述している文字コードを「スクリプトエンコーディング」2という。

Ruby はバージョン 1.9 以降,マジックコメントというものによってスクリプトエンコーディングをあらわに表現できるようになった。

具体的にはスクリプトの第一行に

# encoding: UTF-8

とか

# encoding: CP932

などと書く。

しかし,スクリプトエンコーディングは UTF-8 であることが多いので,Ruby 2.0 以降,デフォルトが UTF-8 になった。

つまり,マジックコメントを略した場合はスクリプトエンコーディングが UTF-8 であると解釈されることになった。

これにより,UTF-8 で書く限りはマジックコメントは不要になったはずである。

ところがそうとも言い切れないよ,というのが本記事の主旨。


開発環境が引き起こす問題

イマドキのテキストエディターや統合開発環境(IDE)では,書いているスクリプトを色分け表示してくれたり,構文上おかしいところなどに下線を引いてエラーメッセージや警告を表示してくれたりする。

ところが,マジックコメント無しだと,正しく書いているのに間違っているとツッコミを入れてくる場合がある。

私が見つけたのは以下のケース。

いずれも Atom 最新版(1.37.0)に linter-ruby パッケージ最新版(1.3.1)を入れてチェックをかけた。(linter-ruby パッケージを使うためには linter パッケージも必要)

スクリプトはすべて UTF-8 で書き,マジックコメントは入れない。

実験は Windows と macOS で行った。

前者 は RubyInstaller for Windows で,後者は rbenv でインストールした,いずれも Ruby 2.6.3 を使用。


ヒアドキュメント

再現コードは以下のとおり。

p <<EOT


EOT

(べつに p は要らないんだけど,入れないと「リテラル使ってないやん」とツッコミが入る)

第 1 行について


unexpected end-of-input, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END


というエラーが出る。

どうやらヒアドキュメント中に非 ASCII 文字があるとダメなようだ。

式展開をしない'EOT' のタイプのヒアドキュメントだとエラーは出ない。

Windows だけかと思ったら macOS でも同じだった。

マジックコメントを付けるとエラーは出なくなる。


正規表現1

再現コードは以下のとおり。

p(/[あ]/)

これは Windows と macOS とで違うエラーが出た。

エラーメッセージをコピペし忘れたが,Windows の場合,「] が無いやんけ」というエラーだったと思う。

これはおそらく以下のような原因だ。

まず  は UTF-8 のバイト列として E3 81 82 である。

Windows ではこれを CP932 として解釈しようとしたようだ。

まず,E3 81 は CP932 で という文字になる。

そして次の 82]5D)とで一つの文字となる。

(0x825D は CP932 の文字として定義されていないがコードポイントとしては存在する)

その結果,正規表現の閉じの ] が無いことになる。

一方,macOS のほうでは,エラーとして /[]/ と表示された。

んあ? よく分からない。文字クラスの中身が空っぽ? もしかしたら ASCII-8BIT として解釈しようとしたのかもしれないが,確かな証拠はない。

Windows でも macOS でも,マジックコメントを付けるとエラーは出なくなる。


正規表現2

再現コードは以下のとおり。

p(/[あかさ]/)

Windows でも macOS でも,前の節の例と同じエラーが出る。

Windows の場合,それに加えて


character class has duplicated range: /[あかさ]/


という警告も出る。

これは以下のような原因であるらしい。

まず,linter-ruby パッケージは,/[abca]/ のような重複のある文字クラスについて,上記のような警告を出す。

あかさ という UTF-8 文字列(3 バイト × 3 文字)は以下のようなバイト列である。

E3 81 82 E3 81 8B E3 81 95

これを CP932 として解釈すると,2 バイト × 3 文字 + 1 バイト × 1 文字となるが,この中に 0xE381 の文字が 2 回出てくるのだ。


VSCode ではどうか

Visual Studio Code ではどうなのか。

Windows で実験してみたが,linter-ruby を使ったのに同じ現象は起きないようだった。

ただ,VSCode は今日初めて使ったので,使い方というか設定の仕方が正しいかどうかよく分からない。

追試頼む。


実行に支障が起きるケース

ここまでに述べたのは,開発環境でのスクリプトのチェックでエラーや警告が出る話だった。

いずれもストレスフルだが,スクリプトの実行自体には差し支えなかった。

しかし,実行自体に問題が起きる場合もある。

これについてはだいぶ前に書いた記事があるので,そちらを参照してほしい。

Rails テンプレートにはマジコメ必要 - Qiita

直接の題材は Ruby on Rails であり,しかも「Rails テンプレート」というマイナーなものに関する報告だった。

しかし,原理的には Rails と無関係に起きうる問題である。

詳細はリンク先を見ていただくとして,手短に書くと,スクリプトを require するのではなく文字列として読み込んで eval する場合に起こりうるという話。





  1. CP932(Codepage 932)は Windows 31J ともいう。Shift JIS(シフト符号化方式の JIS 系文字コード)の一種であり,JIS コード(JIS X 0208)の文字集合に漢字・記号などを追加したもの。 



  2. この記事では「文字コード」と「エンコーディング」は同じと思ってもらって大丈夫。