やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

組込ライブラリ(String)

 文字列。

成果物

情報源

String

文字列のクラスです。ヌル文字を含む任意のバイト列を扱うことができます。文字列の長さにはメモリ容量以外の制限はありません。

文字列は通常、文字列リテラルを使って生成します。以下に文字列リテラルの例をいくつか示します。

文字列リテラルの例

'str\\ing'   # シングルクオート文字列 (エスケープシーケンスがほぼ無効)
"string\n"   # ダブルクオート文字列 (エスケープシーケンスがすべて有効)
%q(str\\ing) # 「%q」文字列 (エスケープシーケンスがほぼ無効、デリミタが変えられる)
%Q(string\n) # 「%Q」文字列 (エスケープシーケンスがすべて有効、デリミタが変えられる)

# ヒアドキュメント
<<End
この行はヒアドキュメント
End

# ダブルクオートヒアドキュメント (クオートなしの場合と同じ)
<<"End"
この行はヒアドキュメント
End

# シングルクオートヒアドキュメント (一切のエスケープシーケンスが無効)
<<'End'
この行はヒアドキュメント
End

# 終端記号がインデントされたヒアドキュメント
# シングルクオート、ダブルクオートとの併用も可能
<<-End
この行はヒアドキュメント (終端記号をインデントできる)
   End

# 中身がインデントされたヒアドキュメント
# シングルクオート、ダブルクオートとの併用も可能
<<~End
  この行のインデントは無視される
End

破壊的な変更

Ruby の String クラスは mutable です。つまり、オブジェクト自体を破壊的に変更できます。

「破壊的な変更」とは、あるオブジェクトの内容自体を変化させることです。例えば文字列のすべての文字を破壊的に大文字へ変更する String#upcase! メソッドの使用例を以下に示します。

例:String#upcase!

a = "string"
b = a
a.upcase!
p a   # => "STRING"
p b   # => "STRING"

この例では、a に対してメソッドを呼んだにも関わらず b も変更されています。これは、変数 a と b が一つの文字列オブジェクトを指していて、 upcase! メソッドでそのオブジェクト自体が変更されたからです。

upcase! の非破壊版である String#upcase を使った例を以下に示します。こちらでは a の変更が b に波及しません。

例:String#upcase

a = "string"
b = a
a = a.upcase
p a   # => "STRING"
p b   # => "string"

一般には、破壊的「ではない」メソッドを中心に使っていくほうがバグが出にくくなります。

String クラスのメソッドには破壊的なメソッドも非破壊的なメソッドもあります。破壊的なメソッドの例としては concat, sub!, upcase! などが挙げられます。非破壊的なメソッドの例としては index, sub, upcase などが挙げられます。

同じ動作で破壊的なメソッドと非破壊的なメソッドの両方が定義されているときは、破壊的なバージョンには名前の最後に「!」が付いています。例えば upcase メソッドは非破壊的で、upcase! メソッドは破壊的です。

ただし、この命名ルールを「破壊的なメソッドにはすべて『!』が付いている」と解釈しないでください。例えば concat には「!」が付いていませんが、破壊的です。あくまでも、「『!』が付いているメソッドと付いていないメソッドの両方があるときは、『!』が付いているほうが破壊的」というだけです。「『!』が付いているならば破壊的」は常に成立しますが、逆は必ずしも成立しません。

言語化と文字列のエンコーディング

String オブジェクトは自身のエンコーディング情報を持ちます。インスタンスメソッドはエンコーディングに従い、1バイトではなく1文字を単位として動作します。エンコーディングの変換にはメソッド String#encode を使います。

例:エンコーディングの変換

p "いろは".size      #=> 3
p "漢字"[0]          #=> "漢"
p "山本山".reverse   #=> "山本山" (回文なので分からないですね)
p "ループ".reverse   #=> "プール"

s = "ruビー"
s[0..1] = ""
p s                  #=> "ルビー"

e = "言語".encode("EUC-JP")
u = "言語".encode("UTF-8")
p e.encoding                   #=> Encoding::EUC_JP
p u.encoding                   #=> Encoding::UTF_8

より詳しく知りたい場合は、多言語化 を参照してください。

文字列同士の比較・結合

文字列同士の比較・結合などでは両者のエンコーディングを意識する必要があります。例えば String#== や String#eql? は両者のエンコーディングが等しくバイト列表現が等しい場合にのみ true を返します。このときエンコーディングUTF-8 であっても正規化せずに比較します。文字列の結合も同様です。異なるエンコーディング同士の文字列を結合する時は明示的にエンコーディングを変換する必要があります。

例:文字列の結合

s = "いろは"
a = s.encode("EUC-JP")
b = s.encode("UTF-8")
p a == b                            #=> false

s = "".encode("EUC-JP")
p s + "\u{4f53}".encode("EUC-JP")   #=> "合体"
p s + "\u{4f53}"                    #=> Encoding::CompatibilityError

String#eql? はハッシュのキーの比較に使われますので、ハッシュのキーに非 ASCII 文字列を使う場合には注意が必要です。

動作例: (注)一行目にmagic commentが必要です。

# encoding: UTF-8
h = {}
s = "いろは"
s.force_encoding("EUC-JP")
h[s] = 1
s.force_encoding("ASCII-8BIT")
p h[s]                             #=> nil

7bit クリーンな文字列

ASCII 互換エンコーディングをもつ 7bit クリーンな文字列はエンコーディングに関わらず ASCII として扱うことができます。例えば String#== は両者の文字エンコーディングが異なっていても true を返します。 ASCII 互換エンコーディングをもつ文字列にエンコーディングの変換なしで結合することができます。

例:

s = "abc"
a = s.encode("EUC-JP")
b = s.encode("UTF-8")
p a == b                           #=> true
p a + b                            #=> "abcabc"

ここで言う「ASCII互換エンコーディング」とは、コードポイントが同一という意味ではなくバイト列が同じことを意味します。従って UTF-16 はASCII互換ではありません。また厳密性を追求せず、おおむね互換なら互換と呼びます。よって Shift_JIS は ASCII 互換です。

バイト列を表す文字列

文字列ではない単なるバイト列も String オブジェクトで表されます。その時のエンコーディングは ASCII-8BIT です。

メンバ抜粋

特異メソッド

new try_convert

インスタンスメソッド

% * + +@ -@ << <=> == === =~ [] []= ascii_only? b bytes bytesize byteslice capitalize capitalize! casecmp casecmp? center chars chomp chomp! chop chop! chr clear codepoints concat count crypt delete delete! delete_prefix delete_prefix! delete_suffix delete_suffix! downcase downcase! dump each_byte each_char each_codepoint each_grapheme_cluster each_line empty? encode encode! encoding end_with? eql? force_encoding getbyte grapheme_clusters gsub gsub! hash hex include? index insert inspect intern length lines ljust lstrip lstrip! match match? next next! oct ord partition prepend replace reverse reverse! rindex rjust rpartition rstrip rstrip! scan scrub scrub! setbyte size slice slice! split squeeze squeeze! start_with? strip strip! sub sub! succ succ! sum swapcase swapcase! to_c to_f to_i to_r to_s to_str to_sym tr tr! tr_s tr_s! undump unicode_normalize unicode_normalize! unicode_normalized? unpack unpack1 upcase upcase! upto valid_encoding?
概要 メソッド
長さを返す size/length, bytesize
文字列を変更する capitalize,

size/length, bytesize

 長さを返す。

メソッド 概要
size/length 文字列長を返す
bytesize バイト長を返す
#coding:UTF-8
p "いろは".size     #=> 3
p "いろは".bytesize #=> 9

置換

メソッド 概要
sub パターンマッチ置換(最初にマッチした箇所のみ)
gsub パターンマッチ置換(すべて)
tr 文字変換(指定した文字を、指定した文字へ変換する
replace 指定した文字列にする
p 'abcdefg-abcdefg'.gsub(/def/, '!!')          # => "abc!!g-abc!!g"
p 'abcdefg-abcdefg'.sub(/def/, '!!')          # => "abc!!g-abcdefg"
p 'abc-abc'.tr('b', 'B')   # aBc-aBc
p 'abc-abc'.tr('ab', 'AB') # ABc-ABc
p 'abc'.replace('def') # def

マッチ部分取得

メソッド 概要
match,match? パターンマッチした要素を返す。マッチしたら真を返す
scan パターンマッチした文字列の配列を返す。
p 'abc123def'.match('\d+')    ##<MatchData "123">
p 'abc123def'.match('\d+')[0] #"123"
p 'abc123def'.scan(/\d+/) # ["123"]

ケース変換

メソッド 概要
capitalize 先頭文字を大文字に、残りを小文字に変更する
downcase 大文字を小文字にする
swapcase 大文字を小文字に、小文字を大文字にする
upcase 小文字を大文字にする
s = 'abc_DEF'
p s.capitalize # Abc_def
p s.downcase   # abc_def
p s.swapcase   # ABC_def
p s.upcase     # ABC_DEF

削除系

メソッド 概要
chomp 末尾から改行コードを削除する
chop 末尾一字を取り除く
delete,delete_prefix,delete_suffix 指定した文字を取り除く
strip,lstrip,rstrip 空白文字を除去(先頭と末尾/先頭のみ/末尾のみ)
squeeze 同じ文字が連続していたらひとつにまとめる
s = "末尾に改行あり\n"
p s       # "末尾に改行あり\n"
p s.chomp # "末尾に改行あり"
s = 'あいうえお'
p s      # "あいうえお"
p s.chop # "あいうえ"
s = 'あいうえおえういあ'
p s.delete ''        # 'いうえおえうい'
p s.delete ''        # 'あいえおえいあ'
p s.delete_prefix '' # 'いうえおえういあ'
p s.delete_suffix '' # 'あいうえおえうい'
s = " \t\nabcdef \t\n"
p s.strip # "abcdef"
s = 'abbbbc'
p s.squeeze # abc

追加

メソッド 概要
insert 指定位置に指定文字列を挿入する
prepent 先頭に指定文字列を挿入する

パディング系

メソッド 概要
center 長さwidthの文字列にselfを中央寄せした文字列を返す
ljust 長さwidthの文字列にselfを左寄せした文字列を返す
rjust 長さwidthの文字列にselfを右寄せした文字列を返す

その他文字列変換

メソッド 概要
reverse 左右逆転する
dump,undump 非表示文字をエスケープ文字にする。それを元に戻す

判定

メソッド 概要
end_with? 末尾が指定した文字列に一致するなら真を返す
start_with? 先頭が指定した文字列に一致するなら真を返す

整数化

メソッド 概要
hex 文字列を16進数として解釈し整数値を返す。
oct 文字列を8進数として解釈し整数値を返す。

分割

メソッド 概要
lines 改行ごとに分割した文字列の配列を返す
split セパレータごとに分割した文字列の配列を返す

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux