rubyのエンコーディング

46
Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9 Rubyのエンコーディング NSEG#30 とみたまさひろ 2012-08-25

Upload: masahiro-tomita

Post on 26-Jun-2015

13.445 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

RubyのエンコーディングNSEG#30

とみたまさひろ2012-08-25

Page 2: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

自己紹介

とみた まさひろ (ペンネーム)✓

プログラマー (Ruby & C)✓

http://d.hatena.ne.jp/tmtms✓

http://twitter.com/tmtms✓

好きなもの

Ruby, MySQL, Ubuntu, Emacs, Git✓

1/45

Page 3: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

NSEG#1 Rubyの黒魔術✓

#3 はじめてのRuby拡張ライブラリ✓

#6 Ruby紹介✓

#11 RSpecとCucumber✓

#13 システムコール✓

#14 MySQLの文字コード✓

#18 Rabbit✓

#20 TDDについて✓

#21 Linuxのメモリ✓ 2/45

Page 4: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Ruby 1.8 は

オワコン3/45

Page 5: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

1.8.7の今後につきまして2012年6月まで、通常のメンテナンスが続きます。

それ以降、バグフィックスリリースは終了いたします。2013年6月まではセキュリティフィックスは続けますので、それまでは1.8.7をお使いいただけます。

2013年6月より先は、1.8.7はあらゆるサポート対象外になります。

http://www.ruby-lang.org/ja/news/2011/10/07/plans-for-1-8-7/ 4/45

Page 6: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Ruby 1.9 を使いまし

ょう5/45

Page 7: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

1.9の特徴

6/45

Page 8: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

一番大きいのはエンコーディング

7/45

Page 9: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Rubyのエンコーディング

http://d.hatena.ne.jp/tmtms/20120812/ruby_encoding

8/45

Page 10: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

エンコーディング?

文字符号化方式✓

UTF-8 とか EUC-JP とか SHIFT_JIS とかそーゆー奴

charset とか呼ばれたり✓

いわゆる「文字コード」✓

9/45

Page 11: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

同じ文字でも異なるコード

「あ」

UTF-8 では 0xE3 0x81 0x82 の3バイト✓

EUC-JP では 0xA4 0xA2 の2バイト✓

SHIFT_JIS では 0x82 0xA0 の2バイト✓

10/45

Page 12: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

同じバイト列でも別の文字

0xC2 0xA9 の2バイトは

UTF-8 では「©」1文字✓

EUC-JP では「息」1文字✓

SHIFT_JIS では「ツゥ」2文字✓

11/45

Page 13: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Ruby 1.8.x

"\xC2\xA9" という文字列は Ruby 的にはただのバイト列

エンコーディング情報を持たない✓

"©"(UTF-8) として扱うか "息"(EUC-JP) として扱うかはプログラム次第

12/45

Page 14: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Ruby 1.9.x

文字列のエンコーディングは文字列自身が知っている

"©"(UTF-8) と "息"(EUC-JP) は同じバイト列だけど異なる文字列

"あ"(UTF-8) と "あ"(EUC-JP) は同じ文字を表してるけど等しくない

同じプログラム中で複数のエンコーディングの文字列を同時に扱える(珍しいかも)

13/45

Page 15: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

ちゃんと文字として扱える

# Ruby 1.8.7"文字".size #=> 6"文字".bytesize #=> 6"文字"[0] #=> 230 (0xE6)"文字".reverse #=> "??????"

# Ruby 1.9.3"文字".size #=> 2"文字".bytesize #=> 6"文字"[0] #=> "文""文字".reverse #=> "字文"

14/45

Page 16: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

String#encoding

文字列のエンコーディングを調べる

"あ".encoding #=> #<Encoding:UTF-8>

15/45

Page 17: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

String#encode

エンコーディング変換

str = "あ"str.encoding #=> #<Encoding:UTF-8>str2 = str.encode("cp932")str2.encoding #=> #<Encoding:Windows-31J>

16/45

Page 18: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

エンコーディングは何で決まる?

スクリプトエンコーディング✓

外部エンコーディング✓

内部エンコーディング✓

17/45

Page 19: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

スクリプトエンコーディング

スクリプト中の文字列リテラルのエンコーディングを決定する

スクリプト先頭行のマジックコメントで指定する

# coding: utf-8"あ".encoding #=> #<Encoding:UTF-8>

デフォルトは US-ASCII (または -K で指定)✓

18/45

Page 20: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

スクリプトエンコーディング

US-ASCII で範囲外の "\xHH" 表記を含む場合は ASCII-8BIT

"\uXXXX" 表記を含む場合は UTF-8

# coding: us-ascii"a".encoding #=> #<Encoding:US-ASCII>"\x41".encoding #=> #<Encoding:US-ASCII>"\xFF".encoding #=> #<Encoding:ASCII-8BIT>"\u3042".encoding #=> #<Encoding:UTF-8>

19/45

Page 21: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

外部エンコーディング

ファイル内の文字列が何のエンコーディングかを指定する

File.open 時に指定する

f = File.open("file.txt", "r:utf-8")f.gets # UTF-8文字列

ファイルの内容は UTF-8✓

読み込んだ文字列も UTF-8 エンコーディング✓

20/45

Page 22: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

外部エンコーディング(書き込み)

f = File.open("file.txt", "w:utf-8")f.puts("ほげ")

文字列が UTF-8 に変換されて書き込まれる✓

21/45

Page 23: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

内部エンコーディング

ファイルから読み込んだ文字列のエンコーディングを変換する

File.open 時に指定する

File.open("file.txt", "r:cp932:utf-8")

ファイルの内容は CP932✓

読み込んだ文字列は UTF-8 エンコーディング✓

書き込み時はあまり関係ない✓

22/45

Page 24: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

もうちょっと詳しく

23/45

Page 25: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

読み込み時の変換

メソッドによって変換したりしなかったり✓

バイナリ読み込みメソッド(常に ASCII-8BIT)

read(n) / read_nonblock / readpartial / sysread

テキスト読み込みメソッド

foreach / readlines / each_line / lines / gets / getc / ungetc / read / readchar / readline / readlines

24/45

Page 26: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

外部エンコーディング未指定時

読み込み時は Encoding.default_external が使われる

Encoding.default_external #=> #<Encoding:UTF-8>

File.open("file.txt", "r:cp932").gets.encoding #=> #<Encoding:Windows-31J>

File.open("file.txt", "r").gets.encoding #=> #<Encoding:UTF-8>

25/45

Page 27: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

Encoding.default_external

-E オプション / ロケール

% ruby -Eutf-8 -e 'p Encoding.default_external'#<Encoding:UTF-8>% LC_ALL=C ruby -e 'p Encoding.default_external'#<Encoding:US-ASCII>% LC_ALL=ja_JP.UTF-8 ruby -e 'p Encoding.default_external'#<Encoding:UTF-8>

かならず値がある✓

外部エンコーディングのデフォルトじゃない✓

26/45

Page 28: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

書き込み時の変換

メソッドには依らない✓

IO に外部エンコーディングが指定されていた場合は変換される

外部エンコーディングが ASCII-8BIT の場合は変換されない

外部エンコーディングが指定されていない場合は変換されない(基本的には)

27/45

Page 29: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

正規表現のエンコー

ディング28/45

Page 30: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

正規表現リテラル

ASCII文字のみの場合は US-ASCII✓

非ASCII文字はスクリプトエンコーディング

# coding: utf-8/あ/.encoding #=> #<Encoding:UTF-8>/./.encoding #=> #<Encoding:US-ASCII>/./n.encoding #=> #<Encoding:US-ASCII>/./s.encoding #=> #<Encoding:Windows-31J>/./e.encoding #=> #<Encoding:EUC-JP>/./u.encoding #=> #<Encoding:UTF-8>

29/45

Page 31: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

正規表現リテラル

US-ASCII で範囲外の "\xHH" 表記を含む場合は ASCII-8BIT

"\uXXXX" 表記を含む場合は UTF-8

# coding: us-ascii/a/.encoding #=> #<Encoding:US-ASCII>/\x41/.encoding #=> #<Encoding:US-ASCII>/\xFF/.encoding #=> #<Encoding:ASCII-8BIT>/\u3042/.encoding #=> #<Encoding:UTF-8>

30/45

Page 32: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

はまりどころ

31/45

Page 33: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

文字列比較

文字が等しくてもエンコーディングが異なれば異なる

u = "あ".encode("utf-8")s = "あ".encode("cp932")u == s #=> false

32/45

Page 34: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

文字列比較

バイト列が等しくてもエンコーディングが異なれば異なる

# coding: utf-8u = "あ"b = "あ".force_encoding("ascii-8bit")u == b #=> false

33/45

Page 35: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

文字列比較

エンコーディングが異なっていてもASCII互換のエンコーディングでASCII文字だけなら比較できる

# coding: utf-8u = "abc".encode("utf-8")s = "abc".encode("cp932")u == s #=> true

34/45

Page 36: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

正規表現

エンコーディングが異なっていたらエラー

# coding: utf-8"あ" =~ /./s# incompatible encoding regexp match (Windows-31J regexp# with UTF-8 string) (Encoding::CompatibilityError)

35/45

Page 37: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

正規表現

ASCII文字だけならエラーにならない

# coding: utf-8"ABC" =~ /./s

ASCII文字だけでテストしたりすると本番で落ちたり

36/45

Page 38: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

文字列結合

異なるエンコーディング同士の結合はエラー

u = "あ".encode("utf-8")s = "あ".encode("cp932")u + s# incompatible character encodings: UTF-8 and Windows-31J# (Encoding::CompatibilityError)

37/45

Page 39: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

文字列結合

ASCII文字だけならエラーにならない

u = "ABC".encode("utf-8")s = "DEF".encode("cp932")u + s #=> "ABCDEF" (UTF-8)

38/45

Page 40: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

リテラル

スクリプトエンコーディングに合わない文字があるとスクリプトのロード時にエラー

# coding: cp932"あ" # UTF-8の「あ」

# invalid multibyte char (Windows-31J)

39/45

Page 41: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

リテラル

でも "\xHH" 表記だとエラーにならない

# coding: cp932"\xE3\x81\x82" # UTF-8の「あ」

40/45

Page 42: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

不正な文字

正規表現との比較でエラー

# coding: cp932str = "\xE3\x81\x82" # ここではエラーにならないstr =~ /./ # invalid byte sequence in Windows-31J (ArgumentError)

41/45

Page 43: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

不正な文字

エンコーディングがあってたとしても不正な文字は発生しうる

# 不正な文字が入っているファイルをUTF-8指定で読んでしまったFile.open("utf-8.txt", "r:utf-8").gets # エラーにはならない

UTF-8 エンコーディングの文字列が得られるが、不正な文字が含まれているかもしれない

42/45

Page 44: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

変換

内部エンコーディングを指定すると不正な文字で変換エラー

# CP932だけど不正な文字が入ってるf = File.open("cp932.txt", "r:cp932:utf-8")f.gets # 不正な文字が含まれる行でエラー# `gets': "\xFC\xA1" from Windows-31J to UTF-8# (Encoding::UndefinedConversionError)

43/45

Page 45: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

変換

外部エンコーディングを指定してないと環境変数の影響うけるかも

内部エンコーディング指定すると自動的に変換されるけど、読むだけでエラーになるかも

44/45

Page 46: Rubyのエンコーディング

Rubyのエンコーディング - NSEG#30 Powered by Rabbit 1.0.9

まとめ

文字単位の処理は便利✓

ハマる時はハマる✓

データのエンコーディングを統一するのが吉✓

45/45