ひがきの日記

2009-02-24

エンコーディングのつづき

ありがたいことに成瀬さんからコメントを頂いたので昨日の続き。

結論から言うと、コメント頂いた通り、スクリプトWindows-31J で書けば、何も迷うことはありません。

環境変数 LANG が ja_JP.SJIS なのは間違ってるけど、Encoding.default_external が Windows-31J になっているので深追いしないことにする。(^_^;

#!/usr/local/bin/ruby19
# -*- coding: cp932; -*-

RUBY_VERSION                    # => "1.9.1"
ENV['LANG']                     # => "ja_JP.SJIS"

__ENCODING__                    # => #<Encoding:Windows-31J>

Encoding.default_external       # => #<Encoding:Windows-31J>
Encoding.default_internal       # => nil
Encoding.locale_charmap         # => "Windows-31J"

`rm -rf encoding.txt`

str = "こんにちは"
str.encoding                    # => #<Encoding:Windows-31J>

open('encoding.txt', 'w'){|f| f.puts str}

buf = open('encoding.txt'){|f| f.read}
buf.encoding                    # => #<Encoding:Windows-31J>

Encoding.compatible?(str.encoding, buf.encoding) # => #<Encoding:Windows-31J>

Regexp.new(str) =~ buf          # => 0

プログラミング言語 Ruby (p.40) を読み直してた。

Ruby 1.9 は、少なくとも、ASCII-8BIT (BINARY とも呼ばれる)、US-ASCII (7 ビット ASCII)、IOS-8859-1*1 から ISO-8859-15 までのヨーロッパエンコーディングUnicodeUTF-8 エンコーディング、日本語エンコーディングSHIFT_JIS (SJIS とも呼ばれる)、EUC-JP をサポートする。手持ちの Ruby 次第では、これ以外のエンコーディングをサポートしている場合がある。
  • Ruby 1.9 は、少なくとも、
  • 手持ちの Ruby 次第では ……

これを読み落したばっかりに Windows-31J で書けないと思い込んでいた。


試してみたところ Meadow*2 の magic comment では、以下は全てエラーになった。

以下なら OK.

  • shift_jis (#<Encoding:Shift_JIS>)
  • cp932 (#<Encoding:Windows-31J>)

動作を見ていると Meadow からは、どちらも Windows-31J らしい。


そして新たな疑問

# -*- coding: cp932; -*-

__ENCODING__                  # => #<Encoding:Windows-31J>
str = 'あいう(1)'
str.encoding                  # => #<Encoding:Windows-31J>
str.valid_encoding?           # => true

str.force_encoding("Shift_JIS")
str.encoding                  # => #<Encoding:Shift_JIS>
str.valid_encoding?           # => true

コード中の (1) は実際には○数字の 1 (0x8740)

最後の String.valid_encoding? は false じゃないの?
やっぱり根本的に Shift_JISWindows-31J を理解できてない?

*1ISO-8859-1 の間違い?

*2Meadow 3.00-dev (KIKU)/emacs 22.1.1/mule 5.0 を使用中。

2009-02-23

エンコーディング

Ruby 1.9.1 のエンコーディングをいろいろ試してみる。

#!/usr/local/bin/ruby19
# -*- coding: shift_jis; -*-

RUBY_VERSION                    # => "1.9.1"
ENV['LANG']                     # => "ja_JP.SJIS"

__ENCODING__                    # => #<Encoding:Shift_JIS>

Encoding.default_external       # => #<Encoding:Windows-31J>
Encoding.default_internal       # => nil
Encoding.locale_charmap         # => "Windows-31J"

`rm -rf encoding.txt`

str = "こんにちは"
str.encoding                    # => #<Encoding:Shift_JIS>

open('encoding.txt', 'w'){|f| f.puts str}

buf = open('encoding.txt'){|f| f.read}
buf.encoding                    # => #<Encoding:Windows-31J>

Encoding.compatible?(str.encoding, buf.encoding) # => nil

Regexp.new(str) =~ buf
# => incompatible encoding regexp match (Shift_JIS regexp with Windows-31J string) (Encoding::CompatibilityError)

Shift_JISWindows-31J は uncompatible incompatible なのね。*1

スクリプトWindows-31J で書くことは ... できないんだろうなぁ。

なんとかファイルから読んだ内容を Shift_JIS にできないものか。

もしかして LANG がまずいのか?
LANG を消して、やってみる。

#!/usr/local/bin/ruby19
# -*- coding: shift_jis; -*-

RUBY_VERSION                    # => "1.9.1"
ENV['LANG']                     # => ""

__ENCODING__                    # => #<Encoding:Shift_JIS>

Encoding.default_external       # => #<Encoding:Windows-31J>
Encoding.default_internal       # => nil
Encoding.locale_charmap         # => "CP932"

`rm -rf encoding.txt`

str = "こんにちは"
str.encoding                    # => #<Encoding:Shift_JIS>

open('encoding.txt', 'w'){|f| f.puts str}

buf = open('encoding.txt'){|f| f.read}
buf.encoding                    # => #<Encoding:Windows-31J>

Encoding.compatible?(str.encoding, buf.encoding) # => nil

Regexp.new(str) =~ buf
# => incompatible encoding regexp match (Shift_JIS regexp with Windows-31J string) (Encoding::CompatibilityError)

Encoding.locale_charmap が違うだけか。

Encoding.default_internal を変更してみる。

#!/usr/local/bin/ruby19 -E:Shift_JIS
# -*- coding: shift_jis; -*-

RUBY_VERSION                    # => "1.9.1"
ENV['LANG']                     # => "ja_JP.SJIS"

__ENCODING__                    # => #<Encoding:Shift_JIS>

Encoding.default_external       # => #<Encoding:Windows-31J>
Encoding.default_internal       # => #<Encoding:Shift_JIS>
Encoding.locale_charmap         # => "Windows-31J"

`rm -rf encoding.txt`

str = "こんにちは"
str.encoding                    # => #<Encoding:Shift_JIS>

open('encoding.txt', 'w'){|f| f.puts str}

buf = open('encoding.txt'){|f| f.read}
buf.encoding                    # => #<Encoding:Shift_JIS>

Encoding.compatible?(str.encoding, buf.encoding) # => #<Encoding:Shift_JIS>

Regexp.new(str) =~ buf          # => 0

これが正解なのかな?

でもこれって、読み込みのタイミングで Windows-31J から Shift_JIS への変換が起ってるんだよねぇ。

サポートレベルが "perhaps" な Cygwin 版だから?
mswin32 版だと違うのかなぁ。

*1Encoding.compatible?("Windows-31J", "Shift_JIS") すると #<Encoding:Shift_JIS> が返ってくるのはバグ?
Encoding.find(name) でエンコーディング名を文字列で渡せるので、Encoding.compatible?(str1, str2) もエンコーディング名を文字列で渡すのかと勘違いした。

2008-08-13

いまさら改行コードの違いに悩む

新しいテスト環境に Cygwin を入れる。

テスト用のシェルスクリプトを動かしたらエラー連発。
どうやら改行コードが原因らしい。

ふつうの Unix
\n
Cygwin
\n
Windows
\r\n

`コマンド` でコマンドの実行結果を取り出したとき、改行をうまく捨ててくれないみたい。

Cygwin の中だけで閉じていると、

$ echo foo
foo
$ echo [`echo foo`]
[foo]
$ echo foo | hexdump
00000000  66 6f 6f 0a
00000004
$ echo [`echo foo`] | hexdump
00000000  5b 66 6f 6f 5d 0a
00000006

となる。

echo foo で "foo\n" を出力するが、`echo foo` は "foo" のみとなる。

ところが、Windows が絡むと、

$ cmd /C echo foo
foo
$ echo [`cmd /C echo foo`]
]foo
$ cmd /C echo foo | hexdump
00000000  66 6f 6f 0d 0a
00000005
$ echo [`cmd /C echo foo`] | hexdump
00000000  5b 66 6f 6f 0d 5d 0a
00000007

cmd /C echo foo で "foo\r\n" を出力し、`cmd /C echo foo` が "foo\r" となる。

昔の Cygwin だと `cmd /C echo foo` も "foo" のみになったんだけどなぁ。


対処方法

bash だけソースからコンパイル & インストールしたら直った。