@kyanny's blog

My thoughts, my life. Views/opinions are my own.

4章 文字列

文字列は String クラスのオブジェクトなので String.new で作れる。他は "", '' などで囲ってリテラル表記。 Perl と同じ。 "" の中のエスケープ文字を解釈するのも '' の中では解釈しないのも Perl と同じ。あと + と << の違いがわかった。 << は破壊的なメソッドなので文字列オブジェクトを変更する。

str = String.new("Hello")
p str + ", World!" #=> "Hello, World!"
p str #=> "Hello"
str << ", World!"
p str #=> "Hello, World!"

あとRuby1.8まではマルチバイト文字列の扱いは、PerlでEncodeを使っていない場合と同じで、単なるバイト列として扱われる。文字列オブジェクト自体が自分自身のエンコーディングを知っていない。Ruby1.9からだと "\uXXXX" とか "\u{XXXX XXXX XXXX}" という表記でリテラルで Unicode 文字を書けるようになったらしい。文字コードはこの章の後のほうでも詳しくでてきた。

式展開というのが便利だ。 Gauche のリーダーマクロ?みたい。 "#{ 好きなRubyの式 }" と書ける。埋め込んだオブジェクトの to_s メソッドを呼び出している。あと $hoge みたいな変数はグローバル変数のようだ。 $foo と書くと常にグローバル変数になるのか慣習なのかはまだ謎。あとの章で出てくるだろう。 "#{ "#{ ... }" }" のように入れ子にできる。便利なときがありそう。

object.inspect メソッドが出てきた。 p メソッドが内部で使ってる。あと Marshal.dump てのが出てきた。何度か見たことがある。 Perl でいうと Data::Dumper 相当ものもかな?と思っていたけどどちらかというと Storable 相当のもののようだ。

バッククォートで囲うと外部コマンドを実行。 Perl と同じ。

% 記法というのが、 Perl の q{}, qq{}, qr{}, qx{} と同じ。対応は、

%q{} ... q{}
%{}, %Q{} ... qq{}
%x{} ... qx{}
%r{} ... qr//
%w{} ... qw()
%W{} ... qw() に似てるが式展開できる
%s{} ... シンボルリテラル(?)

シンボルリテラルがちょっとわからない。あとで。あと大文字小文字で差がある q,Q は覚えづらそう。

ヒアドキュメントもある。 Perl とほぼ同じ。インデントできるらしい。

def foo
    heredoc = <<-HERE
        Hello, World!
        Goodbye, World!
    HERE
end

p foo

当然ながら、 HERE をインデントできるだけなのでヒアドキュメント中に書いた文字列の行頭のスペースなどはそのまんま。

文字リテラル(文字列ではなく)について。Ruby1.8とRuby1.9でかなり変わった部分らしい。どちらも文字リテラルを表記するには ?a のようにする。Ruby1.8だとその文字に対応する整数値を返す。そのバイトの文字コード値。 p ?a #=> 97 となる。 Ruby1.9 だと p ?a #=> "a" となり、その文字一文字だけからなる文字列を返す。 Character クラスのようなものはない。

正規表現。だいたい Perl と同じ。リテラルに //u のように、文字エンコーディングを指定できる。

/あいうえお/ #=> エンコーディングを考慮せず、単なるバイト列の連続に対してマッチ
/あいうえお/u #=> UTF-8 のあいうえお五文字にマッチ
/あいうえお/e #=> EUC-JP のあいうえお五文字にマッチ
/あいうえお/s #=> Shift_JIS のあいうえお五文字にマッチ

Ruby1.9 からは正規表現エンジンがかわって named capture が使えるようになったらしい。 Gauche でもあったな。 Perl でも 5.10 (5.8.x の最新?) とかだと使えるんだっけ。

文字列に による添え字でのアクセスもできるらしい。使うかなぁ。例長いのでパス。

文字列オブジェクトは mutable なので変更可能。この例のために 使うか。 String#gsub, String#gsub! はよく使いそう。 ! がついてるほうが破壊的。ブロックをとる記法も。こちらは覚えづらそう。。

story = <<EOS
Solomon Grundy,
Hello World.
Solomon OK
EOS

story["Solomon"] = "X"
p story

p story.gsub(/Solomon/, "X")
p story
p story.gsub!(/Solomon/, "X")
p story

p story.gsub(/(\S+)/) { "*" * $1.length }
  1. , <<, * などはもう例題で何度か使った。

String#split がでてきた。これも頻繁にお世話になりそうなメソッド。 split! というのはないようだ。

look =  "Look!" * 3
looks = look.split(/!/)
p looks #=> ["Look", "Look", "Look"]
p look #=> "Look!Look!Look!"

比較は Perl の cmp のように専用のものがあるわけではなくて <=> などが使える。他のメソッドはおいおい。

イテレータがでてきた。 each_byte, each_line は前の章で使った。 Ruby1.9 からは each_char というのもある。

sprintf (printf) によるフォーマット指定もお馴染み。あと String#% というメソッドもあり、

p sprintf("%04d", 3)
p "%04d" % 3

この二つが同じ意味になる。 % がでてきてもおどろかないように。

シンボル!でてきた。シンボルリテラルというのがあって、以下のように書く。

:"Anna Terras"
:"#{$$}"
:"\xE3\x81\x82"
:'#{$$}'
:if
:some_method_name

シンボルというのは常に :foo のように、半角英数字と一部の記号のみで構成されるモノなのかと思っていたけど、文字列でもいいらしい。文字列との重要な違いとして、 equal? で比較しても同一とみなされる。

str1 = "ruby"
str2 = "ruby"
p str1 == str2 #=> true
p str1.equal? str2 #=> false

symbol1 = :ruby
symbol2 = :ruby
p symbol1 == symbol2 #=> true
p symbol1.equal? symbol2 #=> true

あとシンボルは immutable だそうだ。そこも文字列と違うところ。それから Ruby1.9 だとハッシュのキーにシンボルを使うとき、 { :symbol1 => "ruby" } のほかに { symbol1: "ruby" } とも書けるらしい。まぁ読み方だけ見ておく。

Ruby1.8 のマルチバイト文字列きた。文字列 = バイト列。 "あいう".length は Perl でいうと bytes::length "あいう" みたいな。 String#upcase など多くのメソッドは ASCII 範囲外のバイトに対しては何もしない。

エンコーディングを明示したり切り替えたりするのに使うのが魔法の $KCODE で、これは規定のエンコーディングを保持するグローバル変数。これの内容を変更することでエンコーディングを切り替える。 $KCODE には以下の値を設定できる。

"NONE" ... エンコーディングを考慮しない。文字列は単なるバイト列として扱われる。 #=> $KCODE = "n"
"SJIS" ... Shift_JIS #=> $KCODE = "s"
"EUC" ... EUC-JP #=> $KCODE = "e"
"UTF8" ... UTF-8 #=> $KCODE = "u"

$KCODE は最初の一文字だけで判定されるので = "u" とかで OK

正規表現はさっき //u とかできるのを見た通り、エンコーディングを考慮してくれる。

# -*- coding: utf-8 -*-
regexp = /(.)/u # UTF8 モードの正規表現オブジェクト
str = "あいうえお"
regexp =~ str
p $1 #=> "あ"

regexp = /(.)/n # バイト列モードの正規表現オブジェクト
regexp =~ str
p $1 #=> "\343"

$KCODE = "u"
regexp = /(.)/ # 規定のエンコーディング == UTF8 モードの正規表現オブジェクト
regexp =~ str
p $1 #=> "あ"

日本語の文字列を一文字ずつ操作、みたいなときは以下のようにする。これはイディオムとしていつも使えるようにしよう。

# -*- coding: utf-8 -*-
"あいうえお".scan(/./u) do |c|
    puts c
end
#=> あ
#=> い
#=> う
#=> え
#=> お

文字列を <=> などで比較する場合、おなじ "あいうえお" でも EUC-JP と Shift_JIS ではバイト列としては別モノなので、バイト列として比較されるので結果は変わってくる。

$KCODE だけでエンコーディングを切り替えるので文字コードが混在してるとまずいことになる。統一重要。

Ruby1.9 では "あいうえお".encoding とかで文字列オブジェクト自身が自分のエンコーディングを知っている。 .length は文字数を返す。バイト数は .bytesize で。

あと Encoding.name_list, Encoding.find("CP1258") など。

マジックコメントはなんか ruby-mode.el とかを使って Emacs でコード書いたら勝手に挿入された。

Ruby1.9 以降でのエンコーディング変換について。 "あいうえお".encode("Shift_JIS") とか。 Perl の Encode モジュールと似ている。これやるとバイト列としても別のものにかわる。バイトはいじらずエンコーディング情報だけかえることもできて、 str.force_encoding("Shift_JIS") とかやるらしい。エンコーディング情報が狂って化けちゃった文字列を元通りにするときに使ったりするのか?

あと、 str_sjis + str_euc とかはできない。違うエンコーディングの文字列同士が連結されて化ける、ということはないらしい。 Perl でいう、 Unicode フラグつきの文字列にそうでない文字列を連結しちゃって勝手にアップグレードされて化ける、みたいなのは起こらないようだ。これは助かりそう。

あと、同じ UTF-8 の "あいうえお" バイト列でも、 Encoding を変えちゃうと別の文字列という扱いになって == で false が返るようになる。

以上、駆け足だったけど4章終わり。 Ruby1.9 での話はあまりコードを打ち込んで試さなかったなぁ。なんとなく、 Perl5.8 のようなもので Ruby1.8 がまだまだ根強く主流としているのじゃないかな、という気がしていて、 1.9 の話はまだ実際に使う機会は少ないのだろうと思っているけど、 Ruby な現場だとどんな感じなんだろう。