Stringクラスの勉強メモ

Ruby始めてそろそろ1年になる(汗)にも関わらず、標準ライブラリについてあまり知らないなって思ったので、改めて勉強しなおすことにしました。
標準ライブラリのメソッドを使いこなせれば、コードはもっと綺麗になる。ってことでまずはStringから!

普段あまり使ってないメソッド

  • %

フォーマットを指定すると、変換結果が返る。フォーマットの形式はsprintfと同じ。

"%#x" % 10   #=> "0xa"
"%#d" % 0xa  #=> "10"
"%#4d"% 10   #=> "  10"

式の中で使う時とかはsprintfよりシンプルでいい

  • count

文字列中の文字の数を返す。0-9, a-z などの指定もできる

"abCdefC".count("C")            #=> 2
"abc123".count("0-9")           #=> 3
"123456789".count("0-9","^4-7") #=> 5
  • slice

self[]と同じ

#指定した位置の文字コードを返す
"test1234".slice(1)     #=> 101
#指定した範囲の文字列を返す
"test1234".slice(1,4)   #=> "est1"
#Rangeで指定した範囲の文字列を返す
"test1234".slice(3..5)  #=> "t12"
#指定した部分文字列を返す。一致するものがなければnilを返す
"test1234".slice("tes") #=> "tes"
#正規表現に一致する最初の部分文字列を返す
"test1234".slice(/\d+/) #=> "1234"

[]よりsliceの方が分かりやすいな。

  • []=val

条件にマッチする部分をvalで置き換える。条件として部分文字列は正規表現を指定した場合は、最初にマッチする部分が置き換えられる

a = "1234abcd1234"
a[/\d+/] = "D" #=> "D"
a              #=> "Dabcd1234"
a = "1234abcd"
a[0, 4] = "X"  #=> "X"
a              #=> "Xabcd"
a              #=> "1234abcd"
a[0..4] = "X"  #=> "X"
a              #=> "Xbcd"

sub, gsubあるから、あまり使わないかも。

  • casecmp

大文字小文字の違いを無視しての文字列比較。辞書順比較で、0,1,-1のどれかを返す

"abcd".casecmp("AbCD") #=> 0
"abcd".casecmp("AbCe") #=> -1
"abce".casecmp("AbCD") #=> 1

0(大文字小文字を意識しない一致)は使えそうだ。

  • center, ljust, rjust

それぞれ中央寄せ、左詰め、右詰めした文字列を返す

"abce".center(10)    #=> "   abce   "
"abce".rjust(10,'*') #=> "******abce"
  • each

区切り文字で指定した文字(デフォルトは$/)で行を分け、各行に対して処理を行う
each_lineも同じ

a="abc\ndef\ghq"
a.each{|line| p line}
"abc\n"
"def\n"
"ghq"
#=> "abc\ndef\nghq"

a.each(","){|line| p line.chomp(",")}
"abc"
"def"
"ghq"
"abc,def,ghq"

splitして処理だろうな普通は

  • hex

文字列を16進数とみて整数に変換

"10".hex  #=> 16
"0xa".hex #=> 10
  • insert

指定した位置に文字列を挿入。self[nth, 0] = valと同じ

a = "ae,ge"
a.insert(3,"hoge,") #=> "ae,hoge,ge"
  • strip

先頭と末尾の空白文字を全て取り除く。空白文字の定義は " \t\r\n\f\v" 。

"  hoge  \n".strip #=> "hoge"

ユーザーが「,」区切りで文字列を入力する場合とかに使える。「a,b,c」と「a, b, c」を同じ物と扱える
lstrip, rstripもある

  • scan

正規表現で繰り返しマッチを行い、マッチした部分文字列の配列を返します。

"q2w3e4r5".scan(/\d/)      #=> ["2", "3", "4", "5"]
"q2w3e4r5".scan(/.\d/)     #=> ["q2", "w3", "e4", "r5"]
"q2w3e4r5".scan(/(.)(\d)/) #=> [["q", "2"], ["w", "3"], ["e", "4"], ["r", "5"]]

splitと違って、マッチした文字列自体で配列を作るのね。
最後の例は配列で分割されているからブロックで多重代入使える。

  • squeeze

連続する文字列を1つにまとめる

"11221122".squeeze("0-9") #=> "1212"
  • tr

文字列を置換する。gsubと違い、正規表現ではなく文字列形式で指定する

"abcd1234".tr("0-9","")        #=> "abcd"
"0a1b2c3d4e5f".tr("0-9","a-z") #=> "aabbccddeeff"
"abcd1234".tr("a-z","A-Z")     #=> "ABCD1234"

最後の例のように複数の文字についてどのように変換するかを指定できる。


こうやってみると、普段はあまり使ってないけど、文字コードを処理したりするコードを見ているとお目にかかるメソッドもいるなー。「%」は「Jpmobile」を読んだ際に始めてしった。

新たな発見があったメソッド

動作の説明はカット。

  • sub, gsub
    • マッチした物は「\&,\0に入る」
a = "abcd1234"
a.gsub(/\d/, '(\&)')   #=> "abcd(1)(2)(3)(4)"

#今までわざわざこうやってた
a.gsub(/(\d)/, '(\1)') #=> "abcd(1)(2)(3)(4)"
    • マッチした文字列自体に処理を加えるならブロックを使う
a.gsub(/\d/){$&.to_i * 2} #=> "abcd2468"

#これをブロック使わないやり方でやろうとすると
a.gsub(/\d/, '\&'.to_i * 2) 
TypeError: can't convert Fixnum into String

#\&という文字がto_iされてしまっている
a.gsub(/\d/, ('\&'.to_i * 2).to_s) #=> "abcd0000"

評価されるタイミングの問題。この文字列が評価される時点ではまだマッチが行われていないので、'\&'は'\&'という文字列そのもの。
マッチした文字列の前に文字列を入れるとかなら(\&)とかでいいけど、文字列自体に処理を加えるならブロックを使う方を使う。

    • 第2引数の指定でよくある間違い

エスケープしないと行けないというのがミソ

'abbb'.gsub(/a(b+)/, "#{$1}")  # これは間違い。まだマッチは行われていない
'abbb'.gsub(/a(b+)/, "\1")     # これも間違い。エスケープされてないので'\1'自体になってしまう
'abbb'.gsub(/a(b+)/, "\\1")    # これは正解
'abbb'.gsub(/a(b+)/, '\1')     # これも正解。''ならおk
'abbb'.gsub(/a(b+)/, '\\1')    # これも正解(より安全)
'abbb'.gsub(/a(b+)/) { $1 }    # これも正解(もっとも安全)
  • split
    • 区切り文字列も結果に含めたいなら「()」を使う
"as-soon_as_po".split(/([-_])/) #=> ["as", "-", "soon", "_", "as", "_", "po"]
    • イディオム(split => collect => joinコンボ)
"as-soon-as-po".split(/\W/).collect{|c| c.capitalize}.join('-') 
#=> "As-Soon-As-Po"

#ちなみに、区切り文字が決まっていないならgsubかscanか
"as_soon-as,po".gsub(/(\w+)(\W*)/){$1.capitalize + $2}
#=> "As_soon-As,Po"
  • delete
a = "abc123"
a.delete("bc12") #=> "a3"
a.delete("a-z")  #=> "123"
a.delete("^a-z") #=> "abc"

a-zとかの指定もできるのね