【Ruby】文字列クラス

「はじめてのRuby」を読んでのメモ。

今回は第14章「文字列(String)クラス」



Ruby での「文字列」はすべて String クラスのオブジェクトである。
String クラスの扱いについて掘り下げていく。

  • 文字列を作る
  • 文字列の長さを得る
  • 文字列のインデックス
  • 文字列の連結と分割
  • 文字列を比較する
  • 改行文字の扱い方
  • 文字列の検索と置換
  • 文字列と配列で共通するメソッド
  • 日本語文字コードの変換


文字列を作る

str1 = "これも文字列"
str2 = 'あれも文字列'
puts "#{str1}です"  #=> これも文字列です
puts '#{str2}です'  #=> #{str2}です



改行(\n)など、\ を使った特殊文字は次の通りである。

特殊文字 意味
\t タブ(0x09)
\n 改行(0x0a)
\r 復帰(0x0d)
\f 改ページ(0x0c)
\b バックスペース(0x08)
\a ベル(0x07)
\e エスケープ(0x1b)
\s 空白(0x20)
\v 垂直タブ(0x0b)
\nnn 8進数表記(n = 0~7)
\Xnn 16進数表記(n = 0~9, a(A)~f(F)
\cx, \C-x [Control] + x
\M-x [Meta]([Alt]) + x
\M-\C-x [Meta]([Alt]) + [Control] + x
\x 文字xそのもの(xは上の文字以外)
\unnnn Unicode 文字の16進数表記(n = 0~9, a(A)~f(F)


%Q,%q

「"」「'」を含んだ文字列を作りたいときに便利である。

puts %Q{Rubyの文字列には「''」も「""」も使われます。}
  #=> Rubyの文字列には「''」も「""」も使われます。
puts %q{Ruby said, 'Hello world!'}
  #=> Ruby said, 'Hello world!'
puts %Q{Ruby\nsaid,\n"Hello!"}
  #=>Ruby
  #=>said,
  #=>"Hello!"
puts %q{Ruby\nsaid,\n"Hello!"}
  #=> Ruby\nsaid,\n"Hello!"


ヒアドキュメントを使う

<< を使って文字列を作るというもの。

<< "終了の記号"
置き換える文字列
終了の記号

という形で使う。終了の記号には「""」か「''」で囲った文字列を書く(どちらもない文字列が使われ場合は「""」で囲った文字列と見なされる)。

10.times do |i|
  10.times do |j|
    print(<<"EOB")
i: #{i}
j: #{j}
i*j = #{i*j}
EOB
  end
end

ヒアドキュメントの終わりに書く文字列は行頭になければならない。インデントを揃えたい場合は、「<<-」を使う。これを使うと、行頭側の空白文字、タブ文字が無視される。

# インデントを揃える
10.times do |i|
  10.times do |j|
    print(<<-"EOB")
i: #{i}
j: #{j}
i*j = #{i*j}
    EOB
  end
end
# 変数に代入
str = <<-EOB
Hello!
Hello!
Hello!
EOB

puts str


printf, sprintf

文字列クラスのメソッドではないが、決められたフォーマットで文字列を出力するためのメソッドである。

n = 123
printf("%d\n", n)

最初の引数が与えられたデータをどのような形で出力するかを指定するフォーマットで、2つ目以降の引数がそのフォーマットの中の「%」のある位置に、順に埋め込まれる。

n = 123
m = 456
printf("%d%d\n", n, m)  #=> 123456
printf("%d\n", n, m)    #=> 123
printf("%d\n%d\n", n)   #=> (ArgumentError)

「%」と「d」の間に文字を加えて、さらなる指定をすることができる。

n = 123
printf("%4d\n", n)
# 4桁で出力
printf("%04d\n", n)
# 指定した桁数に満たない場合は先頭を0で埋める
printf("%+d\n", n)
# 「+」か「-」の記号を必ず出力

出力結果は次のようになる。

123
 123
0123
+123



「d」は整数を出力するための指定用文字で、他にも引数を文字列として解釈させるための指定「s」もある。

n = "Ruby"
printf("%s\n", n)
printf("Hello,%s!\n", n)
printf("Hello,%8s!\n", n)
# "Ruby"を8桁で出力
printf("Hello,%-8s!\n", n)
# 8桁で、左詰めで出力

出力結果はこうなる。

Ruby
Hello,Ruby!
Hello,    Ruby!
Hello,Ruby    !



出力と同様の結果を文字列オブジェクトとして生成するためのメソッドが sprintf である。

p sprintf("%d", 123)           #=> "123"
p sprintf("%4d", 123)          #=> " 123" 
p sprintf("Hello,%s!","Ruby")  #=> "Hello,Ruby!"


文字列の長さを得る

length, size

# 文字列の長さを返す
p "just another ruby hacker,".length  #=> 25
p "just another ruby hacker,".size    #=> 25
p "みなさん、こんにちは。".size          #=> 11  

bytesize

# バイトの長さを返す
p "みなさん、こんにちは。".bytesize  #=> 22

empty?

# 文字列の長さが0なら真を返す
p "".empty?     #=> true
p "foo".empty?  #=> false


文字列のインデックス

puts str[0]     #=> あ
puts str[9]     #=> こ
puts str[-1]    #=> こ
puts str[5..9]  #=> かきくけこ
puts str[0, 5]  #=> あいうえお


文字列をつなげる

文字列をつなげるには、

  • 2つの文字列がつながった文字列を新しく作る
  • 既にある文字列に別の文字列をつなげる

の2つの方法がある。

# 新しい文字列を作る
hello = "Hello,"
world = "world!"

str = hello + world  
p str  #=> "Hello,world!"
# 既にある文字列に別の文字列をつなげる
hello = "Hello,"
world = "world!"

hello << world
p hello  #=> "Hello,world!"
hello.concat(world)
p hello  #=> "Hello,world!world!"

「+」をつかって表現もできる。

hello = "Hello,"
world = "World!"

hello = hello + world
p hello  #=> "Hello,world!"

<< メソッドなどを使うと同じオブジェクトを指す別の変数にも影響がある一方、「+」を使った場合にはそれがない。

hello = "Hello,"
world = "world!"
str = hello

hello = hello + world 
p hello  #=> "Hello,world!"
p str    #=> "Hello,"

hello = "Hello,"
world = "world!"
str = hello

hello << world
p hello  #=> "Hello,world!"
p str    #=> "Hello,world!"


文字列を比較する

「==」や「!=」を使って比較できる。

p "aaa" == "aaa" #=> true
p "aaa" == "aa"  #=> false
p "aaa" != "aa"  #=> true
p "aaa" != "aaa" #=> false

文字列の大小の比較

大小も比較できる。文字の大きい、小さいは文字コード順によって決定される。

p ("aaa" < "b")  #=> true
p ("aaa" > "A")  #=> true


文字列を分割

# 「:」で区切られたそれぞれの文字列を要素とする配列を代入
str = "高橋:タカハシ:25:000-111-000"
column = str.split(/:/)
p column #=> ["高橋", "タカハシ", "25", "000-111-000"]


改行文字の扱い方

each_line などで文字列を読み込んだ場合は末尾に改行文字がつくが、この改行文字が邪魔な場合に改行文字を取り除くメソッドがある。

末尾を必ず1文字削る 改行がある場合のみ削る
非破壊的 chop chomp
破壊的 chop! chomp!


# 末尾が改行文字でない
str = "abcde"
newstr = str.chop
p newstr  #=> "abcd"
newstr  = str.chomp
p newstr  #=> "abcde"

# 末尾が改行文字
str = "abcde\n"
newstr = str.chop
p newstr  #=> "abcde"
newstr = str.chomp
p newstr  #=> "abcde"

each_line メソッドを使う場合には chomp! メソッドなどで破壊的に改行文字を落とすのが常套である。

f.each_line do |line|
  line.chomp!
  line を処理する
end


文字列の検索

文字列の検索

文字列の中に特定の文字列が含まれているかどうかを調べるときは index, もしくは rindex メソッドを使う。rindex の r は right(右) の r。

str = "すももももも"
p str.index("もも")   #=> 1
p str.rindex("もも")  #=> 4
# 見つかった場合は一致部分の先頭のインデックスを返す

p str.index("みかん") #=> nil
# 単純に含まれているかいないかの判別
str = "すももももも"
p str.include?("もも")    #=> true
p str.include?("みかん")  #=> false


文字列と配列で共通するメソッド

インデックス操作に関するメソッド

str = "abcde"
str[2] = "C"
p str  #=> "abCde"
str[1,3] = "BCD"
p str  #=> "aBCDe"
slice!
p str.slice!(-1)    #=> "."
p str.slice!(5, 1)  #=> ","
p str.slice!(0..4)  #=> "Hello"
p str               #=> "Ruby"


Enumerator オブジェクトを返すメソッド

# each_line メソッドで取り出した行を collect メソッドで処理
str = "\n\n\n"
tmp = str.each_line.collect do |line|
  line.chomp * 3
end
p tmp  #=> ["あああ", "いいい", "ううう"]

# each_byte メソッドで取り出した数値を collect メソッドで処理
str = "abcde"
tmp = str.each_byte.collect do |byte|
  -byte
end
p tmp  #=> [-97, -98, -99, -100, -101]


連結や逆順に関するメソッド

concat
s = "ようこそ"
s.concat("ゲストさん")
puts s  #=> ようこそゲストさん
delete

s = "検/索/避/け"
puts s.delete("/") #=> 検索避け
|

reverse
s = "こんばんわ"
puts s.reverse #=> わんばんこ


その他のメソッド

strip
# 先頭と末尾の空白文字を剥ぎ取る
p "  Thank you. "        #=> "  Thank you. "
p "  Thank you. ".strip  #=> "Thank you."
** case, capitalize

case というのは、ここではアルファベットの大文字・小文字の意味。

p "Thank you, Ruby.".upcase      #=> "THANK YOU, RUBY."

p "Thank you, Ruby.".downcase    #=> "thank you, ruby."

p "Thank you, Ruby.".swapcase    #=> "tHANK YOU, rUbY."

p "Thank you, Ruby.".capitalize  #=> "Thank you, ruby."
# 先頭の文字を大文字に、以降の文字を小文字にする
tr
p "ABCDE".tr("A", "a")      #=> "aBCDE"
p "ABCDE".tr("ADE", "ade")  #=> "aBCde"
p "ABCDE".tr("A-E", "a-e")  #=> "abcde"


日本語文字コードの変換

encode

# encoding: EUC-JP
euc_str = "日本語EUCの文字列"
utf8_str = euc_str.encode("utf-8")

encode メソッドで指定できる文字コードの一覧は Encoding.name_list メソッドで取得できる。

nkf ライブラリ

半角カナを全角カナに変換するといった用途に使われる。
nfk メソッドの主なオプションは次の通りである。

オプション 意味
-d 改行文字からCRを削除する
-c 改行文字にCRを加える
-x 半角カナを全角カナに変換しない
-m0 MIME の処理を抑制する
-h1 カタカナをひらがなにする
-h2 ひらがなをカタカナにする
-h3 ひらがなとカタカナを入れ替える
-Z0 JIS X 0208 の数字を ASCII にする
-Z1 -Z0 に加えて全角スペースを半角スペースにする
-Z2 -Z0 に加えて全角スペースを半角スペース2個にする
-e 出力文字コードEUC-JP とする
-s 出力文字コードShift_JIS とする
-j 出力文字コードISO-2022-JP とする
-w 出力文字コードUTF-8(BOM なし)とする
-w 出力文字コードUTF-8(BOM あり)とする
-w80 出力文字コードUTF-8(BOM なし)とする
-w16 出力文字コードUTF-16(Big Endian / BOM なし)とする
-w16B 出力文字コードUTF-16(Big Endian / BOM あり)とする
-w16B0 出力文字コードUTF-16(Big Endian / BOM なし)とする
-w16L 出力文字コードUTF-16(Little Endian / BOM あり)とする
-w16L0 出力文字コードUTF-16(Little Endian / BOM なし)とする
-E 入力文字コードEUC-JP とする
-S 入力文字コードShift_JIS とする
-J 入力文字コードISO-2022-JP とする
-W 入力文字コードUTF-8(BOM なし)とする
-W8 入力文字コードUTF-8(BOM あり)とする
-W80 入力文字コードUTF-8(BOM なし)とする
-W16 入力文字コードUTF-16(Big Endian / BOM なし)とする
-W16B 入力文字コードUTF-16(Big Endian / BOM あり)とする
-W16B0 入力文字コードUTF-16(Big Endian / BOM なし)とする
-W16L 入力文字コードUTF-16(Little Endian / BOM あり)とする
-W16L0 入力文字コードUTF-16(Little Endian / BOM なし)とする



トラブルを避けるため、単純に文字コードを変換することが目的の場合は -x と -m0(まとめて -xm0 と書ける)を常に指定すべきである。

# encoding: EUC-JP
require "nkf"

euc_str = "日本語EUCの文字列"
utf8_str = NKF.nkf("-E -w -xm0", euc_str)

入力文字コードの指定を明示的に行わない場合はライブラリが自動的に判別を行うため、たいてい次のように書くことが出来る。

# encoding: EUC-JP
require "nkf"

euc_str = "日本語EUCの文字列"
utf8_str = NKF.nkf("-w -xm0", euc_str)



日本語の文字に関する特殊な処理を除けば、 encode メソッドを使うのがよい。