RPGツクールVXAceのスクリプト言語であるRGSS3(Ruby1.9.2p0 + ツクールライブラリ)に関する個人メモ。あくまでも個人メモなので管理人以外には編集権限はありません。

×


テキスト(String)

Rubyのテキスト処理

  • Rubyのテキスト処理は基本的にStringクラスのメソッドとして実装されている。
    • 整形文字列はKernelモジュールのメソッドを使うのがメインだが、Stringにも一応ある。
  • シンボル(Symbolクラス)とは共通する処理が多い。文字列定数は基本的にシンボルに置き換えるのが良い。
  • 正規表現(Regexpクラス)やエンコード(Encodingクラス)と連携するメソッドはそれぞれのラッパーとして動作する。
参照: Ruby 1.9.3 リファレンスマニュアル > String
文字列の検索
"abc" =~ '/a/'          #=> 0   正規表現にマッチすればマッチしたインデックス位置を返す マッチしなければ nil
"abc" !~ '/a/'          #=> false 正規表現にマッチしなければ true マッチすれば false
"abc".end_with?("a-c")  #=> true 文字列の末尾が引数のいずれかである時に true
"abc".include?("a")     #=> true 文字列に含まれていれば true
文字列の切り出し
"abcdef"[1]              #=> "b"    文字列を1文字ずつの配列とみなして部分文字列を返す 範囲外の場合は nil を返す
"abcdef"[-1]             #=> "f"    インデックスが負数のときは末尾から数える
"abcdef"[2, 3]           #=> "cde"   インデックス&文字数の指定も可能
"abcdef"[2..3]           #=> "cd"   インデックスには範囲オブジェクトも指定可能
"abcdef"["cd"]           #=> "cd"   文字列から指定文字列を抜き出した新しい文字列を作る 単体では意味なし
"abcdef"[/\w+/]          #=> "abcdef" 正規表現にマッチした文字列を返す
"abcdef"[/(\w)(\w)/, 2]  #=> "b"    正規表現にマッチした2番目のグループの文字列を返す
文字列の置き換え
"abcdef"[1] = "g"              #=> "agcdef"  文字列から任意の1文字を置き換える
"abcdef"[1] = "gh"             #=> "aghcdef" 置き換え文字列の方が長い場合は伸びる
"abcdef"[1..3] = "g"           #=> "agef"   置き換え文字列の方が短い場合は縮む
"abcdef"[2, 3] = "g"           #=> "abgf"   インデックス&文字数の指定範囲への代入も可能
"abcdef"["cd"] = "g"           #=> "abgef"  文字列に含まれる指定文字列を置き換える
"abcdef"[/[cd]/] = "g"         #=> "abgef"  正規表現にマッチした文字列を置き換える
"abcdef"[/(\w)(\w)/, 2] = "g"  #=> "agcdef"  正規表現にマッチした2番目のグループの文字列を置き換える
"abcdef".gsub(/[bdf]/, "5")    #=> "a5c5e5"  正規表現にマッチした文字列を全て置き換える

文字列の追加
"abc" + "def"         #=> "abcdef" 新しい文字列オブジェクトを作る
"abc" << "def"        #=> "abcdef" 左辺に対して破壊的に連結する
"abc".concat("def")   #=> "abcdef" 引数の文字列を破壊的に連結する
文字列の削除
"abc".clear             #=> ""    文字列を空にする
"abc\r\n".chomp         #=> "abc"  文字列から末尾の改行コードを抜いた文字列を返す 改行は引数で指定可能(省略時は$/)
"abc\n\n".chomp         #=> "abc\n" 改行が二つ並ぶ場合は一つしか削除しない点に注意
"abc".delete("b")       #=> "ac"   引数の文字を削除した文字列を返す
"abc\n\n".delete("\n")  #=> "abc"  該当する文字は全て削除する
"\nabc\n".delete("\n")  #=> "abc"  該当する文字は全て削除する
"abc".delete!("b")      #=> "ac"   引数の文字を破壊的に削除する
文字列の比較
"abc" <=> 'abc'       #=> 0   比較した結果を-1,0,1,nilで返す Comparableモジュール用 sortなどの基準となる
"abc".casecmp('Abc')  #=> 0   <=> と同じだが大文字小文字を無視する
"abc" == 'abc'        #=> true  文字列の内容が同じならtrue
"abc" === 'abc'       #=> true  String#=== は == と同じ
"123" == 123          #=> false この場合は 123 == "123" の結果が返される
"abc".eql?('abc')     #=> true  同じ文字列なら true

文字列の正規表現
  • 正規表現の詳しい使い方は別項を参照。
"abc" =~ '/a/'     #=> 0   正規表現にマッチすればマッチしたインデックス位置を返す マッチしなければ nil
"abc" !~ '/a/'     #=> false 正規表現にマッチしなければ true マッチすれば false
"abcdef"[/\w+/]          #=> "abcdef" 正規表現にマッチした文字列を返す
"abcdef"[/(\w)(\w)/, 2]  #=> "b"    正規表現にマッチした2番目のグループの文字列を返す
"abcdef"[/[cd]/] = "g"         #=> "abgef"  正規表現にマッチした文字列を置き換える
"abcdef"[/(\w)(\w)/, 2] = "g"  #=> "agcdef" 正規表現にマッチした2番目のグループの文字列を置き換える
"abcdef".gsub(/[bdf]/, "5")    #=> "a5c5e5" 正規表現にマッチした文字列を全て置き換える
文字列の情報
"abc".ascii_only?   #=> true  文字列がASCII文字のみで構成されていれば true
"abc".size          #=> 3   文字列の文字数 マルチバイト文字も1文字と数える
"abc".length        #=> 3   同上
"abc".bytesize      #=> 3   文字列のバイト長を返す マルチバイト文字はエンコードにより変化
"abc".count("a")    #=> 1   文字列に指定文字がいくつ含まれているかを返す
"abc".count("a-z")  #=> 3   a-zの範囲内の文字の数を返す
"abc".empty?        #=> false 文字列が空(長さ0)なら true
文字列の生成
"abc".center(5)   #=> " abc " 文字列を中央寄せした指定幅の文字列を返す 余白は第2引数で埋められる(省略時は" ")
"abc".ljust(5)    #=> "abc  " 文字列を左寄せした指定幅の文字列を返す 余白は第2引数で埋められる(省略時は" ")
"abc".rjust(5)    #=> "  abc" 文字列を右寄せした指定幅の文字列を返す 余白は第2引数で埋められる(省略時は" ")
"abc".chr         #=> "a"   文字列の最初の文字を返す "abc"[0]と同じ

"abc".capitalize        #=> "Abc"  先頭文字を大文字に他を小文字にした文字列を返す
"ABC".downcase          #=> "abc"  大文字を小文字にした文字列を返す
"abc".upcase            #=> "ABC"  小文字を大文字にした文字列を返す
"aBc".swapcase          #=> "AbC"  大文字と小文字を入れ替えた文字列を返す

"abc\r\n".chomp         #=> "abc"  文字列から末尾の改行コードを抜いた文字列を返す 改行は引数で指定可能(省略時は$/)
"abc\n\n".chomp         #=> "abc\n" 改行が二つ並ぶ場合は一つしか削除しない点に注意
"abc\n\n".delete("\n")  #=> "abc"  該当する文字は全て削除する
文字列の破壊的変更
"abc".clear           #=> ""    文字列を空にする
"abc" << "def"        #=> "abcdef" 左辺のオブジェクトに破壊的に連結する
"abc".concat("def")   #=> "abcdef" 左辺のオブジェクトに破壊的に連結する
"abc".capitalize!     #=> "Abc"   先頭文字を大文字に他を小文字に破壊的に変更する
"ABC".downcase!       #=> "abc"   大文字を小文字に破壊的に変更する
"abc".upcase!         #=> "ABC"   小文字を大文字に破壊的に変更する
"aBc".swapcase!       #=> "AbC"   大文字と小文字を破壊的に入れ替える
"abc\r\n".chomp!      #=> "abc"   文字列から末尾の改行コードを破壊的に削除する
"abc".delete!("b")    #=> "ac"    引数の文字を破壊的に削除する
"abcdef".gsub!(/[bdf]/, "5")  #=> "a5c5e5"  正規表現にマッチした文字列を破壊的に置き換える
文字列の変換
"ff".hex  #=> 255 文字列を16進文字列とみなして数値に変換する

Stringメソッドの詳細

gsubの詳細
# 引数に文字列を渡す場合
s = "abcdef"
s.gsub(/[bdf]/, "5")   #=> "a5c5e5" 正規表現にマッチした文字列を全て置き換えた新しい文字列を返す
s.gsub!(/[bdf]/, "5")  #=> "a5c5e5" 正規表現にマッチした文字列を破壊的に置き換える self | nil を返す

# 引数にHashを渡す場合 複数の置き換えを一気にやりたい場合に便利
s = "abcdef"
h = {"b" => 1, "d" => 2, f: 3}
h.default = "?"
p s.gsub(/[bdf]/, h)   #=> "a1c2e?" 正規表現にマッチした文字列をキーとしてハッシュの値に置き換える
# シンボルキーには対応していない キーが見つからない場合はdefaulfに置き換わる 初期値は空文字
# ""空文字に置き換えた場合は実質的に削除として働く

# ブロックを渡す場合
p s.gsub(/[bdf]/){|k| }  # 正規表現にマッチした文字列をパラメータとしてブロックを評価した値に置き換える

# ブロック応用
p s.gsub(/[bdf]/).with_index{|k,i| idx = Regexp.last_match.begin(0)}
# iでインデックス番号(マッチした回数-1)、idxでマッチ位置を参照可能
文字列の

文字列の


Stringイテレータ

  • 各イテレータでブロックに渡されるデータの違い。
f = proc{|b| format "%0*x", b > 65536 ? 6 : b > 255 ? 4 : 2, b}
a = "alpha漢字\n012"
p a.each_byte.map(&f)       #=> [61,6c,70,68,61,e6,bc,a2,e5,ad,97,0a,30,31,32] ""省略
p a.each_char.to_a          #=> [a,l,p,h,a,漢,字,\n,0,1,2]                     ""省略
p a.each_codepoint.map(&f)  #=> [61,6c,70,68,61,6f22,5b57,0a,30,31,32]         ""省略
p a.each_line.to_a          #=> ["alpha漢字\n","012"]

# 各イテレータに対応する配列を生成するメソッド
p a.bytes       #=> [61,6c,70,68,61,e6,bc,a2,e5,ad,97,0a,30,31,32] 0x省略
p a.chars       #=> [a,l,p,h,a,漢,字,\n,0,1,2]                     ""省略
p a.codepoints  #=> [61,6c,70,68,61,6f22,5b57,0a,30,31,32]         0x省略
p a.lines       #=> ["alpha漢字\n","012"]

整形文字列(sprintf)

  • 数値などを文字として整形するための構文。
  • 文字列指定の%s、整数指定の%d、実数指定の%fの3種だけ覚えておけば99%カバーできる。
# 基本フォーマット
%[引数指定][フラグ][幅][.精度]指示子
# 指定子の種類で使えるフラグや幅、精度の意味は変化する
# 引数指定は引数のうち何を参照するかを指定する 省略時は左から適用する

# 整形メソッドの種類
Kernel#format(format_txt, *argv)   # format_txt が上記フォーマットに対応 *argvは引数
Kernel#sprintf(format_txt, *argv)  # 同上
Kernrl#printf(format_txt, *argv)   # print format(format_txt, *argv) と同等
String#% argv                      # format(self, argv) と同等 引数が複数の場合は配列化が必要

# 例)
p format("%3d:%3d", 50, 80)  #=> " 50: 80" 引数は左から順に参照される
p "%2$3d:%1$3d" % [50, 80]   #=> " 80: 50" 引数指定を変更して順番を入れ替え
p sprintf("(%-5s)", "test")  #=> "(test )" 幅固定文字列も可能
p "%2.4f" % 1.42125          #=> " 1.4213" 小数点は四捨五入される

# サンプル用メソッド
def pf(txt, argv)
  p format(txt, *argv)
end
文字系の指示子
  • 文字列の指示子は c、s、p の三種類。
  • 基本的に s しか使わない。
pf "%c", "a"   #=> "a"  cは単文字(char)
pf "%c", 'a'   #=> "a"  シングルクォートでも同じ
pf "%c", 48    #=> "0"  引数が数値の場合は文字コードとして評価する
pf "%c", 'ab'  #=> Error 単文字や数値以外はエラーする

pf "%s", 48    #=> "48"  sは文字列(string) 引数が文字列ではない場合、to_sメソッドを呼び出す
pf "%s", :sym  #=> "sym"  sはto_s

pf "%p", 48    #=> "48"  pは印刷文字(print) 引数のinspectメソッドを呼び出す
pf "%p", :sym  #=> ":sym" pはinspect

# 文字系のフォーマット
%[引数指定][-][幅]指示子
pf "%4s", 48   #=> "  48" 幅は文字数 不足分は右寄せになり半角空白で埋まる
pf "%-4s", 48  #=> "48  " -は左寄せ
pf "%4c", "a"  #=> "   a" cにも幅は有効
pf "%4p", :sym #=> ":sym" 幅にはプリフィックス(シンボルの:など)も含む
整数の指示子
  • 整数の指示子は d、i、u、b、B、o、x、X の8種類。
  • 実際問題として d しか使わない。稀に X を使う程度。
a = 31
pf "%d", a  #=> "31"   dは整数(decimal) 符号付き10進整数
pf "%i", a  #=> "31"   iは整数(integer) 符号付き10進整数
pf "%u", a  #=> "31"   uは符号なし整数(unsigned) 符号付き10進整数に整形される
pf "%b", a  #=> "11111" bは2進数(binary)
pf "%B", a  #=> "11111" Bは2進数     後述のプリフィックスが大文字
pf "%o", a  #=> "37"   oは8進数(octal) 
pf "%x", a  #=> "1f"   xは16進数(hex)
pf "%X", a  #=> "1F"   Zは16進数     後述のプリフィックスが大文字

# 整数のフォーマット
%[引数指定][-+ 0#][幅][.精度]指示子 # 幅は文字列の幅 精度は数値部の幅
a = 15
pf "%-8.4x", a  #=> "000f    " -は左寄せ
pf "%+8.4x", a  #=> "   +000f" +は符号表示
pf "% 8.4x", a  #=> "    000f" スペースは+の変わりに空白、負数なら-表示
pf "%08.4x", a  #=> "    000f" 0は数値余白を0で埋める
pf "%08x", a    #=> "0000000f" 精度省略時は文字列余白を0で埋める
pf "%#8.4x", a  #=> "  0x000f" #は2進8進16進でプリフィックス表示(16進は 0x か 0X )
pf "%#8.4b", a  #=> "  0b1111" #は2進8進16進でプリフィックス表示(2進は 0b か 0B )
pf "%#8.4o", a  #=> "    0017" #は2進8進16進でプリフィックス表示(8進は 0 )
浮動小数点数の指示子
  • 浮動小数点数の指示子は f、g、e、E、G、a、A の7種類。
  • f だけ覚えれば十分。
a = 1.125
pf "%f", a  #=> "1.125000"   fは浮動小数点数(float) 無指定でも幅8
pf "%e", a  #=> "1.125000e+00" eは指数表現(exponent)
pf "%E", a  #=> "1.125000E+00" Eは指数表現の大文字
pf "%g", a  #=> "1.125"     gは一般型式(general) 指数-4を閾値に f と e を切り替える
pf "%G", a  #=> "1.125"     Gは一般型式の大文字
pf "%a", a  #=> "0x1.2p+0"   aは16進浮動小数点数(another) 0x16進仮数部p+16進指数部というデバッグ用フォーマット
pf "%A", a  #=> "0X1.2p+0"   Aは16進浮動小数点数の大文字 aと合わせて通常使われることは無い
            # 1.2 → 1 + 0.2 → 0.2は16進1の1/8 = 0.125 となる

# 浮動小数点数のフォーマット
%[引数指定][-+ 0#][幅][.精度]指示子 # 幅は文字列の幅、精度は小数部の幅
pf "%#12.4f", a  #=> "      1.1250" 
pf "%#12.4e", a  #=> "  1.1250e+00" 指数部も幅に含む
pf "%#12.4E", a  #=> "  1.1250E+00" 指数部も幅に含む
pf "%#12.4g", a  #=> "       1.125" 精度は有効桁数(1.125は5桁扱い)
pf "%#12.4G", a  #=> "       1.125" 精度は有効桁数(1.125は5桁扱い)
pf "%#12.4a", a  #=> "    0x1.2000p+0" 指数部は幅に含まれない
pf "%#12.4A", a  #=> "    0X1.2000p+0" 指数部は幅に含まれない
pf
pf "%-12.4f", a  #=> "1.1250      " -は左寄せ
pf "%+12.4f", a  #=> "     +1.1250" +は符号表示
pf "% 12.4f", a  #=> "      1.1250" スペースは+の変わりに空白、負数なら-表示
pf "%012.4f", a  #=> "0000001.1250" 0は数値余白を0で埋める
pf "%#12.0f", 1  #=> "          1." #は小数点の強制 fの例
pf "%#12.0e", 1  #=> "      1.e+00" #は小数点の強制 e Eの例
pf "%#12.0g", 1  #=> "          1." #は小数点の強制 g Gの例
pf "%#12.5g", 1  #=> "      1.0000" #は小数点の強制 gの場合は精度が.を含む小数部幅になる
pf "%#12.5f", 1  #=> "     1.00000" #は小数点の強制 fの場合は精度は.を含まない小数部幅
引数指定
  • 引数の順番を入れ替えたり引数からパラメータを得たりできる。
  • 基本的に使うことは無い。
a = [1, 2, 3]
pf "%2$d %3$d %1$d", *a  #=> "2 3 1" n$はn番目の引数を参照する
# 通常は使わないが、元になる配列データにday,manth,yearのようなデータがある際には使えることがある
# とはいえ d,m,y = data からの "%d/%02d/%02d",y,m,d や *data.reverse でも可能なので可読性を考えると微妙

a = {a:1, b:2, c:3}
p format "%<b>d %<c>d %<a>d", h  #=> "2 3 1"  ハッシュのキーで引数を参照する
p format "%{b}d %{c}d %{a}d", h  #=> "2d 3d 1d" ハッシュのキーで引数を参照し指示子パラメータとして展開する 使い道なし

pf "%*d", 4, 2  #=> "   2" *があると引数を指示子パラメータとして展開する

unpack テンプレート文字列

  • String#unpack は Array#pack でパッキングした文字列を配列に戻すメソッド。
    • 端的に言えば pack はバイナリ文字列化する機能で、unpack はバイナリを型に戻す機能。
  • テンプレート文字列は pack、unpack で使用できる文字列。
# メソッド
Array#pack(format)    -> String  # 配列をformatに応じたバイナリ文字列に変換する
String#unpack(format) -> Array   # 文字列をformatに応じた配列に変換する

# format
'i'     # 符号付き整数(システムエンディアン + システムビット数)一文字
'iiii'  # i4文字 Arrayの要素[0..3]をそれぞれiで変換/Stringをiの配列に変換
'i4'    # i4文字 同上 数値で指定可能
'i*'    # 残り全てiとする 可変長に有用
'i>'    # ビッグエンディアン強制 sSiIlL にのみ有効(Ruby1.9.3以降は qQ にも有効)
'i<'    # リトルエンディアン強制 sSiIlL にのみ有効(Ruby1.9.3以降は qQ にも有効)
'i!'    # システムビット強制   sSiIlL にのみ有効(Ruby2.1以降は qQ にも有効)
'i!>'   # システムビット強制 + ビッグエンディアン強制
'i!<'   # システムビット強制 + リトルエンディアン強制
文字列のテンプレート
'a'  # ASCII文字列(null文字を詰める / unpack時は後続するnull文字やスペースを残す) 
'A'  # ASCII文字列(スペースを詰める / unpack時は後続するnull文字やスペースを削除) 
'Z'  # pack時は不足分をnull文字で埋める / unpack時は後続するnull文字を削除) 'z'は無効
'b'  # 2進数文字列 下位ビット→上位ビット 使い道は不明
'B'  # 2進数文字列 上位ビット→下位ビット 通常はこちらだけでよい
'h'  # 16進数文字列 下位ニブル→上位ニブル 使い道は不明
'H'  # 16進数文字列 上位ニブル→下位ニブル 通常はこちらだけでよい
'c'  # char型 符号付き8ビット整数
'C'  # char型 符号無し8ビット整数
整数のテンプレート
'i'  # 符号付きシステムエンディアンシステムビット整数 int型(RGSS3ではリトル32ビット)
'I'  # 符号無しシステムエンディアンシステムビット整数 int型
's'  # 符号付きシステムエンディアン16ビット整数 short型
'S'  # 符号無しシステムエンディアン16ビット整数 short型(word型)
'l'  # 符号付きシステムエンディアン32ビット整数 long型
'L'  # 符号無しシステムエンディアン32ビット整数 long型(dword型)
'q'  # 符号付きシステムエンディアン64ビット整数 qword型 RGSS3では強制エンディアン無効
'Q'  # 符号無しシステムエンディアン64ビット整数 qword型 RGSS3では強制エンディアン無効
'v'  # 符号無しリトルエンディアン16ビット整数 VAXバイトオーダー
'V'  # 符号無しリトルエンディアン32ビット整数 VAXバイトオーダー リトル64ビット型を扱う場合は Q で代用可能
'n'  # 符号無しビッグエンディアン16ビット整数 Netバイトオーダー
'N'  # 符号無しビッグエンディアン32ビット整数 Netバイトオーダー ビッグ64ビット型は無いので N2 ぐらいしかできない
'm'  # base64文字列に変換 端的に言えば64進数文字列
'M'  # quoted-printable encoding文字列に変換 ASCII以外を16進文字列変換
浮動小数点数のテンプレート
'f'  # se32 システムエンディアン単精度実数(32ビット) 'F'も同様
'd'  # se64 システムエンディアン倍精度実数(64ビット) 'D'も同様
'e'  # le32 リトルエンディアン単精度実数(32ビット)
'E'  # le64 リトルエンディアン倍精度実数(64ビット)
'g'  # be32 ビッグエンディアン単精度実数(32ビット)
'G'  # be64 ビッグエンディアン倍精度実数(64ビット)
その他のテンプレート
'p'  # NULL終端文字列のポインタ     WIN32APIなどで使われる
'P'  # 構造体(固定長文字列)のポインタ WIN32APIなどで使われる
'u'  # Unix-to-Unixエンコード文字列   現在ではbase64が使われる
'U'  # UTF-8
'w'  # base128(可変長整数) ツクール2000などで使われていたBER圧縮整数
'x'  # pack時はNULL文字追加 / unpack時は1文字飛ばす
'X'  # バックスペース(手前の文字を1文字削除)
'@'  # pack時は絶対位置へ移動(余白はNULLで埋める) / unpack時は複合位置の変更
参照: Ruby 1.9.3 リファレンスマニュアル > pack テンプレート文字列

シンボル(Symbol)

シンボルとは

  • シンボルはRubyの特徴的な仕組みで、簡単にいえば軽量文字列と表現できる。
  • シンボル名に対して一意の数値が割り当てられ、データ上は単なる整数(Fixnum)として扱われるため、文字列のわかりやすさと数値のメモリ効率/処理速度の高さを両立している。
    • ソース上では文字列のようだが内部的には数値であるため同じシンボルには常に同じオブジェクトが割り当てられる。
    • 特にハッシュのキーや文字列定数の代替として利用すると非常に効果が高い。
    • これらにシンボルを使うのはRubyの基本テクニックなので必ず覚えたい。
  • ただしRGSS3の場合、シンボルを管理しているシンボルテーブルはGCの対象とならないため注意が必要。
    • シンボルテーブルは一度使用したシンボルを全て記憶しているため、無制限にシンボルを生成するようなコードはメモリを圧迫する原因となる。
    • シンボルテーブルはセーブデータには記録されないため再起動を挟めばリセットされるが、F12リセットでは初期化されないため注意。
:symbol    # シンボルは :(コロン)+名前で指定する
:シンボル  # 日本語シンボルも利用可能
:":sym"    # コロンを含むシンボルも使えるがメリットが薄いので使うべきではない

# シンボルをハッシュキーとして使う
a = {:key1 => 1, :key2 => 2}  # ハッシュのキーにはシンボルが適する
a = {key1:1, key2:2}          # キーがシンボルの場合は略記構文を使える
a[:key1]                      #=> 1  

# シンボルを使った定数配列
OPTION = [:msg_speed, :msg_fast, :command_keep, :dash_always]
参照: Ruby 1.9.3 リファレンスマニュアル > Symbol

シンボルと文字列の関係

  • シンボル :name は基本的に文字列 name に対応する。
  • SymbolクラスとStringクラスは共通のメソッドが多く、用途次第ではシームレスに扱う事も可能。
:name.id2name #=> "name" シンボルを文字列に変換
:name.to_s    #=> "name" 同上
'name'.to_sym #=> :name  文字列をシンボルに変換
'name'.intern #=> :name  同上

# SymbolとStringをシームレスに扱う例 以下の二つは同じ結果を返す
p method("class").call
p method(:class).call

# SymbolクラスとStringクラスの共通のメソッド
# !~ <=> == =~ [] slice capitalize casecmp downcase empty? encoding
# to_s inspect intern to_sym length size next succ swapcase upcase
:name.to_s     #=> "name"
"name".to_s    #=> "name"
:name.to_sym   #=> :name
"name".to_sym  #=> :name
:name.upcase   #=> :NAME
"name".upcase  #=> "NAME"

:abcde =~ /cd/       #=> 2 シンボル頭の : はマッチング対象にならない点に注意
"abcde" =~ /cd/      #=> 2
"abcde" =~ /#{:cd}/  #=> 2 正規表現にシンボルを使う場合は式展開を利用する
:abcde =~ /k/        #=> nil
"abcde" =~ /k/       #=> nil
/cd/ =~ :abcde       #=> 2 左右を入れ替えても結果は同じ
/cd/ =~ "abcde"      #=> 2
/#{:cd}/ =~ "abcde"  #=> 2
/k/  =~ :abcde       #=> nil
/k/  =~ "abcde"      #=> nil

# SymbolクラスとStringクラスの同名で処理の異なるメソッド
# match
:abcde.match(/cd/)   #=> 2                      =~ と同じ挙動
"abcde".match(/cd/)  #=> MatchDataオブジェクト Regexp#match(String)と同じ
/cd/.match(:abcde )  #=> SyntaxError            Regexp#matchはシンボルを引数に取れない
/cd/.match("abcde")  #=> MatchDataオブジェクト

# ===
:abcde  === /[a-c]+/  #=> SyntaxError Symbol#=== は定義されていない
"abcde" === /[a-c]+/  #=> true         String#=== はRegexp#=== を呼び出す
/[a-c]+/ === :abcde   #=> true        Regexp#=== はシンボルも受け付ける
/[a-c]+/ === "abcde"  #=> true        Regexp#=== は文字列とマッチする

# 上記違いを吸収するためのメタプログラミング例
class Symbol
  # matchの挙動をRegexp#matchを呼ぶように変更 既存と挙動が変わるので注意
  def match(reg)
    reg.match(self.to_s)
  end
  # ===を実装
  def ===(other)
    case other
    when Symbol   then other == self          # Symbolとは同値判定
    when String   then other == self.id2name  # Stringとは同名判定
    when Regexp   then other === self         # Regexpはマッチ判定
    when Array    then other.include?(self)   # Arrayはinclude判定
#   when MetaData then other.include?(self)   # MetaDataはinclude判定 ※RGSS3プログラミング参照
    else false
    end
  end
end
参照: RGSS3プログラミング#metatag

SymbolとStringの比較

  • 文字列とシンボルは同じように使えるが比較では(当然だが)同一とはみなされない。
    • シンボルを文字列に変換、もしくは文字列をシンボルに変換して比較する事で同一とみなす事もできる。
    • シンボルを文字列に変換する場合、変換のたびに新しい文字列オブジェクトが作成されるためメモリ効率があまり良くない。
    • 文字列をシンボルに変換する場合、作成されるシンボルオブジェクトは文字列ごとに一度だけだが、シンボルオブジェクトとシンボル名を結びつけるためのシンボルテーブルは(Ruby1.9.2時点では)GC対象とならないため使われる予定のない不要なシンボル情報が増え続けてメモリの圧迫/処理速度の低下を引き起こす問題がある。
    • 他には正規表現によるマッチングが挙げられるが、内部的にはSymbol#to_s.matchを呼び出しているため遅くなるだけでto_sと変わらない。
  • シンボル情報はセーブデータには記録されないので、String#to_sym を使い続けても再起動を挟めばリセットされるがあまりお勧めはできない。
# SymbolとStringの比較
:name == "name"         #=> false 文字列とシンボルの比較は equal?、eql?、==、=== いずれも false となる
:name == "name".to_sym  #=> true 文字列をシンボルに変換して比較
"name" == :name.to_s    #=> true シンボルを文字列に変換して比較

# GCを考慮すると短時間しか稼動しないプロセス、もしくは比較文字列の種類が少ない場合には String#to_sym の方が良い。
# 逆に長時間稼動するプロセス、もしくは比較文字列の種類が多い場合には Symbol#to_s の方がメモリへの悪影響は少ない。
# 動作するプログラムに応じてケースバイケースで使い分ける必要がある。
# RGSS3の場合はよほど変な実装をしない限りは前者のパターンでも問題ない。心配なら後者で実装すると良い。

Symbol.all_symbolsに含まれる要素

  • Symbol.all_symbolsはシンボルテーブルを可視化する。つまり実際にプログラム内で定義されているシンボルが全て含まれる。ここに含まれる情報は以下の通り。
    • 全ての定数名(クラス名、モジュール名も含む)
    • 全てのメソッド名(aliasメソッドや、アクセサによるメソッドも含む)
    • 全てのグローバル変数名
    • 全てのクラス変数名
    • 全てのインスタンス変数名(クラスインスタンス変数含む)
    • 全てのローカル変数名(引数名やブロックパラメータも含む)
    • 一部の擬似変数名や予約語(selfやbeginなど)
    • Ruby内部のC実装で使用される特殊シンボル
    • 全てのシンボルリテラル(Structのメンバ名なども含む)
    • 動的に参照されるシンボル名(動的にシンボルに変換された文字列なども含む)
  • RGSS3環境だと起動直後でも軽く2000は超える。
    • RGSS3環境ではシンボル情報は使われなくなってもメモリに残り続け、GC対象とならない。そのため特に動的なシンボル生成(文字列からの変換など)は極力行うべきではない。
# プログラム内で使われている全シンボルリストをファイルに書き出すコード
open('symbol.log', 'w'){|f| f.puts Symbol.all_symbols.sort }

記法

リテラル記法

  • 文字列リテラルの書き方は下記3種類。
'text'   #=> "text" シングルクォートで囲む 文字列はそのままの形で使用される
"text"   #=> "text" ダブルクォートで囲む 文字列は後述する\記法や式展開を評価して決定される
?a       #=> "a"    ? に続けて文字表現を書く 1文字を表現するための記法
?\n      #=> "\n"   文字表現は\記法も評価される
?\
         #=> "\n"   ?\直後に改行を入れると改行文字とみなされる
"0" 'a'  #=> "0a"    文字列リテラルを並べた場合、結合された文字列としてパースされる
                  #  式展開や%記法を含む文字列は結合されない
記法名称略称別名
''シングルクォート文字列sq文字列プレーンテキスト
""ダブルクォート文字列 wq文字列拡張テキスト
?文字リテラル     ?記法 単文字表現

\記法(バックスラッシュ記法)

  • \(バックスラッシュ)に続けて英数字を書くことで制御文字や文字コードを表現する記法。
    • RGSS3では基本的に \n ぐらいしか使わないので、他は覚えなくてもいい。
    • 稀に直接記述できないUNICODE文字を表現したい場合に \uHHHH を使うぐらい。
メタ文字意味和訳8進表現16進表現コードポイント
\abellベル\007\x07\u0007
\bback spaceバックスペース\010\x08\u0008
\ttab(horizontal tab)水平タブ\011\x09\u0009
\nnewline改行\012\x0A\u000A
\vvertical tab垂直タブ\013\x0B\u000B
\fform feed改ページ\014\x0C\u000C
\rreturn復帰\015\x0D\u000D
\eescapeエスケープ文字\033\x1B\u001B
\sspace半角空白文字\040\x20\u0020
\\backslash半角¥マーク\134\x5C\u005C
# コード表現
\            # エスケープ文字 \に続く文字を表す \自体や#などを表現するのに使う
\nnn         # 8進数表現 (nnn は8進数3文字 000-177の範囲のみ、超えるとSyntaxError)
\xHH         # 16進数表現 (HH は16進数2文字 00-7Fの範囲のみ、超えるとSyntaxError)
\uHHHH       # コードポイント (HHHH は16進数4桁のUNICODEコードポイントを指定)
\u{H H ....} # コードポイント文字列 (H は16進数1桁から6桁までのUNICODEコードポイントを指定)

# テキスト内改行
"1st\        # ダブルクォート文字列中に\nを含めずに改行したい場合は末尾に\で改行する
2nd"         #=> "1st2nd"

式展開

  • ダブルクォート文字列内に #{式} を挿入し評価結果を挿入することができる。
p "#{1+3}"  #=> "4"
p '#{1+3}'  #=> "\#{1+3}" シングルクォート文字列は式展開されない
n = "Ruby"
v = "1.9.2"
p "#{n+v}"  #=> "Ruby1.9.2"

%記法

  • %記法はシングルクォート文字列、ダブルクォート文字列、正規表現、コマンド出力、配列式などのブラケットを別の記号に変更できる記法。
  • 通常は必要ないが、例えばクォート文字自体を含む文字列をエスケープ無しで定義したり、文字列配列定義時にクォート文字やカンマを省略して簡潔に書けるようになる。
# %記法例
%!STRING!   #=> "STRING" ダブルクォート文字列
%:STRING:   #=> "STRING" :で挟む事もできる
%(STRING)   #=> "STRING" 括弧で囲む場合、対応する閉じ括弧で挟む必要がある
            # 基本的に何で囲んでもいいがシフトを使わずに入力できる記号にするとやや捗る ただし紛らわしいのは注意
%|STRING|   #=> "STRING" |で挟むとブロックパラメータっぽいので紛らわしい
%[STRING]   #=> "STRING" []で挟むと配列式っぽいので紛らわしい
%#STRING#   #=> "STRING" #で挟むとコメントっぽいので紛らわしい
%/STRING/   #=> "STRING" /で挟むと正規表現っぽいので紛らわしい

%q!STRING!  #=> 'STRING'と同じ シングルクォート文字列 式展開、\記法は使えない
%Q!STRING!  #=> "STRING"と同じ ダブルクォート文字列  式展開、\記法を使用可能
%x!STRING!  #=> `STRING`と同じ コマンド出力      式展開、\記法を使用可能
%r!STRING!  #=> /STRING/と同じ 正規表現        式展開、\記法を使用可能
%s!STRING!  #=> :STRING と同じ シンボル        式展開、\記法は使えない
%s!:STRING! #=> :":STRING" と同じ シンボルの場合%記法を使う意味は薄い

# %配列記法 ブラケットを[]にするとわかりやすいのでオススメ
%w[ST RI \n]  #=> ["ST", "RI", "\\n"] 式展開、\記法は使えない
%W[ST RI \n]  #=> ["ST", "RI", "\n"]  式展開、\記法を使用可能

正規表現(Regexp)

  • ここでは主にRGSS3で使える正規表現について述べる。
    • 正規表現は様々なバージョンがあり、使えるコードが異なるので注意。
    • RGSS3のベースであるRuby1.9.2が採用しているのは鬼車+鬼雲パッチの一部。
  • 正規表現は非常に強力なテキスト検索機能である。様々な使い道ができるが、だからといって何でもかんでも正規表現で解決しようとするのは間違いであることを留意しておきたい。
    • 例えば正規表現には文頭にマッチする ^、文末にマッチする $ がある。これらは「改行が含まれる場合に不正利用されることがあるため文字列の先頭と末尾にマッチする \A \z を使うように」と解説される事が多い。しかしそもそもの話、改行を正規表現に渡されて困るなら正規表現で処理するよりも前に改行を排除すべきだ。危険なコードを問題が起こる可能性があるとわかっている処理にフィルターを通さずに流すなど、そもそも設計が間違っている。
      • 更にいえば \A\z を使ったとしても文字列先頭と末尾に \n が含まれない事を保証できるだけで文字列中の \n を無力化できるわけではないため、単純に ^$ を \A\z に置き換えればセキュリティが向上するというものではない。言い換えれば txt.match(/\Apat\z/) は txt.chomp.match(/^pat$/) と同義でしかない。
    • 正規表現は処理系ではない。小規模な構文解析にのみ使用するのが正しく安全な使い方である。
    • 実際のセキュリティ運用についてはここでは割愛するが、最低限「正規表現に含まれて困る文字は正規表現で解析する前に除外する」という事だけは徹底すべき。
    • 正規表現は存在そのものがバギーなので、使い方を誤らないようにしたい。
参照: Ruby1.9.2 正規表現
参照: Ruby 1.9.3 リファレンスマニュアル > Regexp

正規表現の安全な使い方

  • 正規表現は以下の3パターンでのみ使うのが望ましい。これを超えるような処理が必要になった場合それは正規表現の領分ではないと言っていい。できるできないの話ではなく、やるべきではない。
    • 単純なバリデーション。ガード句などによる早期リターンの条件式など。
    • 置き換え元文字の探索。gsub!やdelete!など。
    • 単純なペアの抽出。"key=value"のような文字列から、keyを探索してvalueを切り出す処理。
  • 逆に正規表現で行うべきではない代表的な処理は以下になる。
    • .(ピリオド)でのマッチ。これが最もバギーな正規表現。実際にマッチしたい文字を正しく指定するべき。
    • mオプションが必要な処理。これらはeach_line等で分割して処理するべき。改行を含めたまま正規表現で処理するのは設計ミス。
    • 制御文字を含む文字列の処理。最初にバリデーションで弾いて一切扱わないのが良い。また無理にサニタイズせずにそもそも扱いを中止するべき事案。
    • ネストする構文の解析。専用パーサを作るかもしくは専用ライブラリを活用するべき。XMLやjsonなら大抵専用ライブラリがある。
    • utf-8ではない文字列の処理。どうしようもない場合を除いてutf-8にエンコードしてから処理するべき。特にRubyでは他のエンコードを正規表現に流す意味がない。
    • xオプションによる複数行にまたがる正規表現、コメントがないと理解できない正規表現。これらはそもそも書くべきではない。

正規表現の書き方

  • Rubyにおける正規表現はネイティブ実装されている。
    • 正規表現リテラルとしても表現可能なので特別な用意なく利用できる。
    • 正規表現はRegexpクラスの機能として実装されている。正規表現リテラルはRegexpオブジェクトとなる。
    • StringやSymbolなどのオブジェクト側にも正規表現メソッドが用意されており、引数として正規表現を渡せるものも数多くある。
      • これらは基本的にRegexpオブジェクトのラッパーメソッドとして実装されているものが多い。
# 基本的な使い方
p /[a-z]+/ =~ "012abc"  #=> 3 abc にマッチ
p "012abc" =~ /[a-z]+/  #=> 3 abc にマッチ 自身を引数に右辺の =~ を呼び出す
p /[a-z]+/.match("012abc")  #=> #<MatchData "abc">
p "012abc".match(/[a-z]+/)  #=> #<MatchData "abc"> 自身を引数に Regexp#match を呼び出す

# メソッドによる違い
p /[a-z]+/.match("012abc")  #=> #<MatchData "abc"> matchはマッチ成功したらMatchDataオブジェクトを返す 失敗したらnilを返す
p /[a-z]+/ =~ "012abc"      #=> 3 =~はマッチ成功したらマッチした位置(文字列インデックス)を返す 失敗したらnilを返す
p /[a-z]+/ === "012abc"     #=> true ===はマッチが成功したかどうかの真偽値を返す

# 正規表現に関係する特殊変数
# これらの特殊変数は正規表現がマッチ成功するたびに上書きされる マッチ成功しなければ上書きされない
/([a-z]+):(\d+)/ =~ "012abc:456!?:"
$~  #=> #<MatchData "abc:456" 1:"abc" 2:"456"> 最後にマッチ成功した正規表現のMatchDataオブジェクト Regexp.last_match と同等
$&  #=> "abc:456" 最後にマッチ成功したマッチ文字列全体 Regexp.last_match[0] と同等
$+  #=> "456" 最後にマッチ成功した最後のグループ文字列 Regexp.last_match[-1] と同等
$`  #=> "012" 最後にマッチ成功したマッチ文字列の前の文字列 Regexp.last_match.pre_match と同等
$'  #=> "!?:" 最後にマッチ成功したマッチ文字列の後の文字列 Regexp.last_match.post_match と同等
$1  #=> "abc" 最後にマッチ成功した1つ目のグループ文字列 Regexp.last_match[1] と同等 $2以降も同等

# 正規表現オプション
# リテラルの末尾に半角英字で指定する i m o x u s e n などがある
/[a-z]+/i =~ "012ABC"  #=> 3 ABC にマッチ iは大文字小文字を無視する
# オプションは他にもあるがRGSS3ではほぼ i しか使わないので割愛する
正規表現リテラルと正規表現文字列
  • 正規表現にはリテラル(Regexpオブジェクト)と文字列の2種類が存在する。
    • 一部のメソッドは文字列しか扱えない場合がある。
    • 文字列はリテラルに比べて表現できる情報に制限がある。例えばUNICODEプロパティやPOSIXは使用できない。

代表的な正規表現文字列

  • 特に使用頻度の高いものだけを抜粋。全てを網羅はしないので注意。
    • ここに掲載されていない表現が必要になった場合はまずコード全体の設計を見直すべき。
    • 基本的に正規の使い方で正規表現を使っている場合、ここに掲載されている内容だけで事足りるはず。
    • これ以上の表現や複雑な判定が必要な場合には制御構文などを併用した方が可読性およびメンテナンス性が高くなる場合が多い。
  • 否定文字クラスは制御コードなどの特殊文字も含んでしまう点に注意。これらが混じる可能性がある場合は事前に処理する事が望ましい。
# 基本形
/pattern/  # 正規表現デリミタ / / で囲われた範囲が正規表現のパターンを記述するエリアとなる

# 文字
/a b/     # 文字列に "a b" が含まれる場合にマッチする
          # 半角スペースはデリミタではなく半角スペースそのものにマッチするので注意
          # xオプション付きだとスペースを無視する
/日本語/  # 文字列に "日本語" が含まれる場合にマッチする マルチバイト文字も利用可能
/\\/      # エスケープ文字 \記号を表すには \ でエスケープし \\ と記述する
          # \記号は特殊文字のエスケープの他に、メタ文字(後述)の表現にも使用する

# 特殊文字
\     # エスケープ文字 特殊文字などにマッチしたい場合に \. のような形で指定する \自身は \\ と書く
.     # 改行以外の任意の一文字(マルチバイト文字含む)にマッチする mオプションがあると改行にもマッチする
|     # orデリミタ 主にグループ内で左右のいずれかにマッチする条件指定に使用する
()    # グループ化 正規表現をまとめ、マッチ内容を記憶して再利用するために使用する
(?:)  # 単純グループ化 マッチ内容を記憶せずにグループ化する際に使用する 若干負荷が低いが可読性が悪い

# 文字クラス
/[0123456789]/  # [ ] で囲われたエリアは文字クラスと呼ばれる 文字のいずれかにマッチする
/[0-9]/         # -記号を用いて文字範囲を指定できる 0-9 なら0123456789のいずれかにマッチする
/[0-9a-zA-Z_]   # 複数範囲も指定可能
/[^0-9]/        # 文字クラスの先頭に ^ がある場合は否定文字クラスとなる 0-9以外の全ての文字にマッチする
/[ぁ-ん]/       # マルチバイト文字にも対応
/[0-9&&[^5]]/   # &&は複合条件を指定する 0-9のうち5以外にマッチする [0-46-9]と同等
/[^0-9&&[^5]]/  # 否定文字クラスに否定を重ねた場合、否定を否定する 0-9以外と5にマッチする

# メタ文字
\d  # 文字クラス[0-9]の短縮形(10進数値) ※RGSS3では全角数字にはマッチしない
\h  # 文字クラス[0-9a-fA-F]の短縮形(16進数値) ※RGSS3では全角文字にはマッチしない
\w  # 文字クラス[0-9a-zA-Z_]の短縮形 ※RGSS3では全角文字にはマッチしない(付属ヘルプの誤植)
\s  # 文字クラス[ \t\v\n\r\f]の短縮形 半角スペースを含んでいる点に注意(公式リファレンスでは\vが記載洩れ)
\D  # 否定文字クラス[^0-9]の短縮形
\H  # 否定文字クラス[^0-9a-fA-F]の短縮形
\W  # 否定文字クラス[^0-9a-zA-Z_]の短縮形
\S  # 否定文字クラス[^ \t\v\n\r\f]の短縮形

# アンカー(文字ではなく位置にマッチするメタ文字)
^   # 文字列の先頭にマッチする 改行文字を含む場合は改行文字の直後にマッチする
$   # 文字列の末尾にマッチする 改行文字を含む場合は改行文字の直前にマッチする
\A  # 改行を無視した文字列の先頭にマッチする \aはbellのメタ文字なので注意
\z  # 改行を無視した文字列の末尾にマッチする
\Z  # 文字列の末尾と末尾の改行の直前にマッチする
\b  # \wと\Wの間にマッチする         具体的には \w\b\W または \W\b\w ※文字クラス内ではバックスペース
\B  # \w同士や\W同士の間にマッチする 具体的には \w\B\w または \W\B\W
\G  # 前回マッチ部分の直後にマッチする 正規表現イテレートする場合に使用する

# 量指定子(最長マッチ)
*      # 直前の表現の0回以上の繰り返し
+      # 直前の表現の1回以上の繰り返し
?      # 直前の表現の0〜1回の繰り返し
{n}    # 直前の表現のn回の繰り返し
{n,}   # 直前の表現のn回以上の繰り返し
{n,m}  # 直前の表現のn〜m回の繰り返し

# 量指定子(最短マッチ)
*?      # 直前の表現の0回以上の繰り返し(最短マッチ)
+?      # 直前の表現の1回以上の繰り返し(最短マッチ)
??      # 直前の表現の0〜1回の繰り返し(最短マッチ)
{n}?    # 直前の表現のn回の繰り返し(最短マッチ)
{n,}?   # 直前の表現のn回以上の繰り返し(最短マッチ)
{n,m}?  # 直前の表現のn〜m回の繰り返し(最短マッチ)

# コード表現
\nnn    8進数表現 (nnn は8進数3文字 000-177の範囲のみ、超えるとSyntaxError)
\xHH    16進数表現 (HH は16進数2文字 00-7Fの範囲のみ、超えるとSyntaxError)
\uHHHH  コードポイント (HHHH は16進数4桁のUNICODEコードポイントを指定)
\u{H H ....} コードポイント文字列 (H は16進数1桁から6桁までのUNICODEコードポイントを指定)
メタ文字意味和訳8進表現16進表現コードポイント
\abellベル\007\x07\u0007
\bback spaceバックスペース\010\x08\u0008
\ttab(horizontal tab)水平タブ\011\x09\u0009
\nnewline改行\012\x0A\u000A
\vvertical tab垂直タブ\013\x0B\u000B
\fform feed改ページ\014\x0C\u000C
\rreturn復帰\015\x0D\u000D
\eescapeエスケープ文字\033\x1B\u001B
エスケープが必要な文字
  • 正規表現中にエスケープが必要な文字のリスト。
# 正規表現リテラルの / / 内でエスケープが必要な文字
( ) [ ] . * + { } ^ $ ? | / \

# 文字クラス [ ] 内でエスケープが必要な文字
[ ] / \
[ 直後の ^           # [a^] はエスケープ不要
[ ] と隣接しない -   # [-a] や [a-] はエスケープ不要
グループ化の再利用
/(\d)(\d)(\d)/ =~ "123"
p $1  #=> "1" $1は一つ目の( )にマッチした文字列を参照できる
# 正規表現がマッチするとグループごとに $1 $2 ...と特殊変数にマッチ内容が代入される
# これらを参照することでマッチした値をグループごとに個別に取り出せる
# 仕様上、直前にマッチ成功した正規表現しか参照できない点に注意

/(\d)+/ =~ "123"
p $1  #=> "3" グループを量指定子で繰り返した場合、最後にマッチした値になる点に注意
# 言い換えれば正規表現のグループ番号はレキシカルスコープで決定されると言える
最長マッチと最短マッチの違い
/<.*>/.match  "<abc> <012>"  #=> #<MatchData "<abc> <012>"> 最長マッチ
/<.*?>/.match "<abc> <012>"  #=> #<MatchData "<abc>"> 最短マッチ
文頭マッチ(^ \A)の違いと文末マッチ($ \Z \z)の違い
p /^[a-z]+/   =~ "abc"  #=> 0 abc の手前にある文頭にマッチ
p /\A[a-z]+/  =~ "abc"  #=> 0 abc の手前にある文字列の先頭にマッチ
p /[a-z]+$/   =~ "abc"  #=> 0 abc に続く文末にマッチ
p /[a-z]+\z/  =~ "abc"  #=> 0 abc に続く文字列の末尾にマッチ
p /[a-z]+\Z/  =~ "abc"  #=> 0 abc に続く文字列の末尾にマッチ
p /^[a-z]+/   =~ "\nabc"    #=> 1   abc の手前にある改行の直後にマッチ
p /\A[a-z]+/  =~ "\nabc"    #=> nil abc の手前に文字列の先頭が存在しない(間に改行がある)ためマッチしない
p /[a-z]+$/   =~ "abc\n"    #=> 1   abc に続く改行の手前にマッチ
p /[a-z]+\z/  =~ "abc\n"    #=> nil abc に続く文字列の末尾が存在しない(間に改行がある)ためマッチしない
p /[a-z]+\Z/  =~ "abc\n"    #=> 1   abc に続く文字列の末尾の改行の手前にマッチ
p /[a-z]+\Z/  =~ "abc\n\n"  #=> nil abc に続く改行が文字列の末尾ではないためマッチしない

p /^.+$/m    =~ "\nabc\n\n"  #=> 0 "\nabc\n\n"にマッチ マルチラインモードでは.が\nも巻き込むので注意
p /\A.+\z/m  =~ "\nabc\n\n"  #=> 0 "\nabc\n\n"にマッチ マルチラインモードでは.が\nも巻き込むので注意
p /\A.+\Z/m  =~ "\nabc\n\n"  #=> 0 "\nabc\n\n"にマッチ マルチラインモードでは.が\nも巻き込むので注意
正規表現検索範囲の視覚化マッチ備考
/^.+$/"\n123\nabc\n""123"マッチするのは最初にマッチした部分のみ
/\A.+\z/"\n123\nabc\n"nil文字列の先頭と末尾
/\A.+\Z/"\n123\nabc\n"nil文字列の先頭と末尾改行の手前
/^.+\Z/"\n123\nabc\n""abc"文頭と末尾改行の手前
/^.+$/m"\n123\nabc\n""\n123\nabc\n"マルチラインモードでは.が\nも巻き込む
/\A.+\z/m"\n123\nabc\n""\n123\nabc\n"マルチラインモードでは.が\nも巻き込む
/\A.+\Z/m"\n123\nabc\n""\n123\nabc\n"マルチラインモードでは.が\nも巻き込む
/^.+?$/m"\n123\nabc\n""\n123"マルチライン+最小マッチ
/\A.+?\z/m"\n123\nabc\n""\n123\nabc\n"マルチライン+最小マッチ
/\A.+?\Z/m"\n123\nabc\n""\n123\nabc"マルチライン+最小マッチ

UNICODEプロパティ

  • メタ文字 \p{} にプロパティ名を含める事でUNICODEプロパティによる文字表現を利用できる。基本的に半角全角問わず含まれる。
  • 利用可能なプロパティは膨大なので代表的なものだけを掲載する。詳細は参照リンクを参照。
  • カテゴリは書かなくて良い。\p{name} でプロパティ名か別名を指定する。RGSS3では大文字小文字を問わない。
    • 否定する場合は \p{^name} もしくは \P{name} もしくは [^\p{name}] で指定する。
  • General_Categoryの大分類(別名が1文字のプロパティ)は文字に対して一意であり複数のカテゴリにバインドされている文字は存在しない。これを組み合わせれば思い通りの組み合わせの文字を表現できる。
  • UNICODEプロパティ、および後述のPOSIXは正規表現リテラルでは扱えるが、正規表現文字列(delete!など)では扱えない点に注意。
カテゴリプロパティ名別名主な文字備考
BinaryASCIIASCIIASCII文字[\x00-\x7F]と同等
ScriptHiraganaHiraひらがなRGSS3ではーは含まれない
ScriptKatakanaKanaカタカナRGSS3ではーは含まれない �猫舛覆匹盍泙�
ScriptHanHani漢字日本だけでなく中国なども含む
ScriptCommonZyyy数字 記号 空白 制御文字 絵文字ーも含まれる
General_CategoryLetterL単語構成文字数字や記号、空白は含まない 〇以外の漢数字を含む
General_CategoryNumberN数字 数字を含む文字Nd + Nl + No 〇以外の漢数字は含まれない
General_CategoryDecimal_NumberNd普通の数字123132など
General_CategoryLetter_NumberNl文字数字�記境鍬検擦覆�
General_CategoryOther_NumberNoその他の数字½��㈠など
General_CategorySeparatorZ各種空白タブを含まない空白文字のみ
General_CategoryPunctuationP句読点記号類Pc + Pd + Ps + Pe + Pi + Pf + Po
General_CategoryConnector_PunctuationPc接続記号アンダーバーやタイなど
General_CategoryDash_PunctuationPdダッシュ記号ハイフン(マイナス記号)など
General_CategoryOpen_PunctuationPs開き括弧([{「『《【 など
General_CategoryClose_PunctuationPe閉じ括弧)]}」』》】 など
General_CategoryInitial_PunctuationPi先頭引用符‟ など
General_CategoryFinal_PunctuationPf末尾引用符” など
General_CategoryOther_PunctuationPoその他の句読点#!"%&'*\,./:;?@ 、。 など
General_CategorySymbolS句読点以外の記号Punctuationと合わせればほぼ全記号を網羅できる
$^`|~+<=> ×÷↑≒⇔など 算術記号のうち-だけPunctuationな点に注意
General_CategoryMarkM装飾文字囲み文字やアクセント記号など他の文字と組み合わせて1文字に見せる文字
日本語環境だとほぼ使わないので無視しても問題ない
General_CategoryOtherC制御文字関連全てCc + Cf + Cs + Co + Cn
General_CategoryControlCc制御文字[\x00-\x1f\u007f-\u009f]と同等
General_CategoryFormatCfフォーマット制御文字BiDi制御文字や絵文字合成などに使われる非文字
General_CategorySurrogateCsサロゲートコードポイント2文字分で1文字を表現する拡張領域 utf-8では使用禁止領域
General_CategoryPrivate_UseCoプライベート使用文字アプリケーションごとに独自文字を表現する際に使える拡張領域
General_CategoryUnassignedCn未割り当て予約済み未割り当てコードポイントまたは非文字
参照: http://www.unicode.org/reports/tr44/#General_Categ...
参照: https://util.unicode.org/UnicodeJsps/list-unicodes...
# 実行サンプル
def test(ptn, txt)
  a = txt.each_char.select{|c| ptn.match c }
  p a ? a.join : nil
end
sample = "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC\
!#&[]<=>^|_-+*/!#&「」<=>^|_−+*/ \t \n☆■・"
test(/\p{ASCII}/, sample)        #=> "012abcABC!#&[]<=>^|_-+*/ \t\n"
test(/\p{Hira}/, sample)         #=> "ひら"
test(/\p{Kana}/, sample)         #=> "カナ"
test(/\p{Han}/, sample)          #=> "漢字〇一二零壱弐"
test(/\p{Common}/, sample)       #=> "ー012012!#&[]<=>^|_-+*/!#&「」<=>^|_−+*/ \t \n☆■・"
test(/\p{Letter}/, sample)       #=> "ひらーカナ漢字一二零壱弐abcABCabcABC"
test(/\p{Number}/, sample)       #=> "〇�記境�012012"
test(/\p{Separator}/, sample)    #=> "  " 半角スペース+全角スペース
test(/\p{Punctuation}/, sample)  #=> "!#&[]_-*/!#&「」_−*/・"
test(/\p{Symbol}/, sample)       #=> "<=>^|+<=>^|+☆■"
test(/\p{Mark}/, sample)         #=> nil
test(/\p{Control}/, sample)      #=> "\t\n"
POSIX文字クラス
  • 文字クラス内で使用可能なPOSIX構文。POSIXクラス名を [:name:] で囲んで指定する。
    • 否定する場合は [:^name:] もしくは [^[:name:]] で指定する。
  • 文字クラスの [] とは意味が異なる。実際に正規表現に組み込む際は [[::]] と文字クラスの中にPOSIX文字クラスを記述する。ようするにメタ文字と同じ扱い。
  • POSIX文字クラスはUNICODEプロパティに対する略記法でもある。
クラス名主な構成文字備考
[:ascii:]ASCII[\x00-\x7F]と同等
[:digit:]Decimal_Number一般的な数字 漢数字や1/2などは含まれない
[:alpha:]Letter + Nl + Mark名前に反してアルファベットだけではなく各国の単語構成文字にマッチ
[:alnum:]alpha + digit記号を除くほとんどの文字にマッチ
[:word:]alnum + 接続文字接続文字はアンダーバーやリエゾンなど
[:punct:]Punctuation句読点全て(括弧や"、ハイフン、アンダーバーなども含む)
[:cntrl:]Other制御文字、フォーマット、サロゲート、プライベート、予約域
[:blank:]Space_Separator + \t基本空白文字と水平タブのみ
[:space:]Separator + \s簡単に言えば \s のマルチバイト空白対応版
[:graph:][space + Cc + Cs + Cn]以外空白と制御文字を除く有効な文字全て(フォーマットやプライベート含む)
[:print:]graph + 空白基本的には空白を含む印刷可能な文字を全て含む
# 実行サンプル
def test(ptn, txt)
  a = txt.each_char.select{|c| ptn.match c }
  p a ? a.join : nil
end
sample = "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC\
!#&[]<=>^|_-+*/!#&「」<=>^|_−+*/ \t \n☆■・"
test(/[[:ascii:]]/, sample)  #=> "012abcABC!#&[]<=>^|_-+*/ \t\n"
test(/[[:digit:]]/, sample)  #=> "012012" 漢数字の〇を含まない
test(/[[:alpha:]]/, sample)  #=> "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�abcABC"
test(/[[:alnum:]]/, sample)  #=> "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC"
test(/[[:word:]]/, sample)   #=> "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC__"
test(/[[:punct:]]/, sample)  #=> "!#&[]_-*/!#&「」_−*/"
test(/[[:cntrl:]]/, sample)  #=> "\t\n"
test(/[[:blank:]]/, sample)  #=> " \t "
test(/[[:space:]]/, sample)  #=> " \t \n"
test(/[[:graph:]]/, sample)  #=> "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC!#&[]<=>^|_-+*/!#&「」<=>^|_−+*/☆■・"
test(/[[:print:]]/, sample)  #=> "ひらーカナ漢字〇一二零壱弐�記境沓瓧癸磽腺贈�012012abcABC!#&[]<=>^|_-+*/!#&「」<=>^|_−+*/  ☆■・"

式展開

  • 正規表現には式展開も使用できる。
  • 式展開が含まれる正規表現リテラルは評価されるたびにオブジェクトが生成される点に注意。
    • oオプション指定時には一度しか生成されないが最初の値で固定化されるため定数展開時ぐらいしか役に立たない。
  • 文字列を式展開する際に ""ダブルクォート文字列と ''シングルクォート文字列では挙動が異なる。(下記参照)
# 文法
/#{txt}/  # Stringの場合、"" で囲まれたエスケープ文字は展開時に外れるので '' で囲うのが良い
r = /\d+/          # 正規表現文字列(Regexp#source)が展開される
s = '\d+'          # プレーンテキストが展開される
w = "\d+"          # \記法(バックスラッシュ記法)テキストとして展開される
a = "012abcd"
p /#{r}/ .match a  #=> #<MatchData "012"> parsed: /\d+/
p /#{s}/ .match a  #=> #<MatchData "012"> parsed: /\d+/
p /#{w}/ .match a  #=> #<MatchData "d">   parsed: /d+/

o = "#{s}"  # \記法でプレーンテキストを式展開
p /#{o}/ .match a  #=> #<MatchData "012"> 式展開の入れ子も可能だが可読性が著しく低下するのでやるべきではない

正規表現サンプル

Rubyソース解析例
/^[ \t]*/         # 行頭のタブまたはスマートタブ(半角空白)にマッチ \sは余計なものもマッチするので良くない
/^ */             # Rubyは基本的にスマートタブなので半角空白のみでも良い
/(module|class)/  # module か class にマッチ
/(\w+(::\w+)*)/   # 参照デリミタを含むクラス名or定数 グループの入れ子は外側から番号が割り当てられる
                  # 端的にいえば開始括弧の位置がグループ番号になる
/^ *(module|class) +(\w+(::\w+)*)/  # module構文またはclass構文にマッチ $1がクラス名(モジュール名)

/(\w+[!?]?)/         # メソッド名にマッチ(末尾に ! か ? を含む場合もマッチ)
/($|[;( #]|\b[^.])/  # 行末もしくは引数やコメントの区切り、もしくは.が無い場合にマッチ
                     # 単語境界\bに続いて否定ピリオド[^.]を指定すればピリオドを含む場合にマッチしなくなる
/^ *def +(\w+[!?]?)($|[;( #]|\b[^.])/    # インスタンスメソッド定義文にマッチ      $1がメソッド名
/^ *def +(\w+\.\w+[\!\?]?)/              # 特異メソッド定義文にマッチ          $1がメソッド名
/^ *def +((\w+(::\w+)*)\.(\w+[\!\?]?))/  # 参照デリミタを含む特異メソッド定義文にマッチ $1がメソッド名全体
                                         # $2がクラス名部分、$4がメソッド名部分

# 演算子メソッド例 | でor評価する場合、表現に重複部分がある場合は長い方を先に書く
/^ *def +(\[\]=|\[\])\(/          # []= []     参照演算子メソッドにマッチ   $1がメソッド名
/^ *def +(<=>|===|==|!=)\(/       # <=> === == != 比較演算子メソッドにマッチ   $1がメソッド名
/^ *def +(!~|=~)\(/               # !~ =~     正規表現演算子メソッドにマッチ $1がメソッド名
/^ *def +(<=|>=|[<>])\(/          # <= >= < >   関係演算子メソッドにマッチ   $1がメソッド名
/^ *def +(<<|>>|[|&^])\(/         # << >> | & ^  ビット演算子メソッドにマッチ  $1がメソッド名
/^ *def +(\*\*|\-|[*+\/%])\(/     # ** - + * / %  算術演算子メソッドにマッチ   $1がメソッド名
/^ *def +(-@|\+@|[!~])($|[; #])/  # ! ~ +@ -@   単項演算子メソッドにマッチ   $1がメソッド名
正規表現ガードサンプル
# ガード用パース
/[\x00-\x1f]/           # 制御文字(ASCII範囲)全てにマッチ ただしRGSS3では\eを\退避に使っているので注意
/[\x00-\x1a\x1c-\x1f]/  # 制御文字(ASCII範囲)全てにマッチ \e除外版
/[\x00-\x1f&&[^\e]]/    # 制御文字(ASCII範囲)全てにマッチ \e除外版
/[^[:print:]]/          # 制御文字(マルチバイト含む)全てにマッチ プライベート域など必要なブロックは含まない
/[[:print:]]/           # 制御文字(マルチバイト含む)を除外した文字全てにマッチ
/[ -~]/                 # 有効なASCII文字のみにマッチ ※[\x20-\x7e]と同等
/[\p{Hira}ー]/          # ひらがな構成文字のみにマッチ −(長音)も含める
/[\p{Kana}ー]/          # カタカナ構成文字のみにマッチ −(長音)も含める

# バリデーション例
return if txt =~ /[[:cntrl:]]/                           # テキストに制御文字が含まれていたらリターン
return if txt !~ /^[a-zA-Z_]{3}(\d{2})/ || $1.to_i > 20  # 4〜5文字目が数字以外もしくは数値が20以上ならリターン

# サニタイズ(無害化)例
txt.gsub!(/[[:cntrl:]]/, "")                # テキストから制御文字を削除する
txt.gsub!(/[[:cntrl:]&&[^\e\p{Co}]]/, "")   # テキストから制御文字(\eとプライベート領域以外)を削除する
# RGSS3で不正な文字がデータに含まれる可能性は極めてゼロに等しいがタブなどはコピペ等で混じる可能性はある
# タブを除外するついでに制御文字を全て除外しておくのは悪い選択肢ではない
メモ欄用サンプル
/\:([^\s\d][[:graph:]]*)/       # シンボル文字列にマッチ($1は : を含まない、to_sym用)
/\#\{([[:print:]]*)\}/          # 式展開の中身にマッチ(instance_eval($1)用)
/<#{txt}>/                      # <> で囲まれた文字列 txt にマッチ(メモ欄マッチング用)
/<#{txt}[ =:]+([-]?[\d\._]+)>/  # <> で囲まれた文字列 txt = value の形式にマッチし、$1で数値を得る、メモ欄マッチング用)
数値リテラルのパースサンプル
  • 下記の例は数値リテラル文字列から各数値に自動変換する際の記述例となる。
  • 実用レベルではここまでやる理由はない。
    • 前述のように単に ([-]?[\d\._]+) とだけ指定してメモ欄に記述する際に気をつけるだけでいい。
    • もしくは正規表現を通さず丸ごとevalに渡したほうが早い。ツクールのような個人開発ならメモの書き方を注意するほうが遥かに簡単だ。
/^[+-]?\d+(_\d+)*(\.\d+(_\d+)*)?([eE][+-]?\d+(_\d+)*)?$/  # 浮動小数点数リテラル文字列にマッチ to_f で変換可能 指数表現や-0.0にも対応
/^[+-]?0+$/                       # 0にマッチ これは以降の正規表現におけるイレギュラーを排除する重要な役目がある
/^[+-]?0x\h+(_\h+)*$/             # 16進整数リテラル文字列にマッチ to_i(16) もしくは to_i(0) で変換可能
/^[+-]?(0o|0)[0-7]+(_[0-7]+)*$/   # 8進整数リテラル文字列にマッチ to_i(8) もしくは to_i(0) で変換可能
/^[+-]?0b[01]+(_[01]+)*$/         # 2進整数リテラル文字列にマッチ to_i(2) もしくは to_i(0) で変換可能
/^[+-]?(0d\d+|[1-9]\d*)(_\d+)*$/  # 10進整数リテラル文字列にマッチ to_i(10) もしくは to_i(0) もしくは to_i で変換可能
式展開を利用した可読性向上のイディオム
D10    = /(_\d+)*/                # 符号なし10進整数 _含む
DG8    = /[0-7]+/                 # 符号なし8進整数
DG2    = /[01]+/                  # 符号なし2進整数
D10PRE = /[+-]?(0d\d+|[1-9]\d*)/  # 符号 + 10進数プリフィクス部
DG8PRE = /[+-]?(0o|0)/            # 符号 + 8進数プリフィクス部
DG2PRE = /[+-]?0b/                # 符号 + 2進数プリフィクス部
F_INT  = /[+-]?\d+#{D10}/         # 符号 + 浮動小数点数の仮数部の整数部
F_DEC  = /(\.\d+#{D10})?/         # 浮動小数点数の仮数部の小数部
EXP    = /([eE][+-]?\d+#{D10})?/  # 浮動小数点数の指数部
/^#{F_INT}#{F_DEC}#{EXP}$/     # 浮動小数点数リテラル文字列にマッチ
/^#{DG8PRE}#{DG8}(_#{DG8})*$/  # 8進整数リテラル文字列にマッチ
/^#{DG2PRE}#{DG2}(_#{DG2})*$/  # 2進整数リテラル文字列にマッチ
/^#{D10PRE}#{D10}$/            # 10進整数リテラル文字列にマッチ
メタプログラミングを活かしたRubyらしい可読性向上のイディオム
class Regexp
  def +(reg)
    Regexp.compile(self.source + reg.source)
  end
  def |(reg)
    Regexp.union(self, reg)
  end
end

D10    = /(_\d+)*/                 # 符号なし10進整数 _含む
DG8    = /[0-7]+/                  # 符号なし8進整数
DG2    = /[01]+/                   # 符号なし2進整数
D10PRE = /^[+-]?(0d\d+|[1-9]\d*)/  # 符号 + 10進数プリフィクス部
DG8PRE = /^[+-]?(0o|0)/            # 符号 + 8進数プリフィクス部
DG2PRE = /^[+-]?0b/                # 符号 + 2進数プリフィクス部
F_INT  = /^[+-]?\d+#{D10}/         # 符号 + 浮動小数点数の仮数部の整数部(integer)
F_DEC  = /(\.\d+#{D10})?/          # 浮動小数点数の仮数部の小数部(decimal)
EXP    = /([eE][+-]?\d+#{D10})?/   # 浮動小数点数の指数部(exponent)
D8BODY = /#{DG8}(_#{DG8})*/        # 8進数構成表現 _含む
D2BODY = /#{DG2}(_#{DG2})*/        # 2進数構成表現 _含む
EOS    = /$/                       # 文末(end of sentence)

p D10PRE + D10 + EOS     =~ "78560"  #=> 0    =~  は式のままでも判定可能
p D10PRE + D10 + EOS    === "78560"  #=> true === も式のままでも判定可能
p(D10PRE + D10 + EOS).match "78560"  #=> #<MatchData "78560"> 式のままmatchを使うにはグループ化が必要

RegexpFloat = F_INT + F_DEC + EXP + EOS  # 浮動小数点数リテラル文字列にマッチ
RegexpOct   = DG8PRE + D8BODY + EOS      # 8進整数リテラル文字列にマッチ
RegexpBin   = DG2PRE + D2BODY + EOS      # 2進整数リテラル文字列にマッチ
RegexpInt   = D10PRE + D10 + EOS         # 10進整数リテラル文字列にマッチ

p RegexpFloat.match "1.5e2"  #=> #<MatchData "1.5e2">
p RegexpOct.match   "03507"  #=> #<MatchData "03507">
p RegexpBin.match   "0b101"  #=> #<MatchData "0b101">
p RegexpInt.match   "78560"  #=> #<MatchData "78560">
xオプションを利用した可読性向上のイディオム
  • xオプションは半角空白と物理改行を無視するようになるオプション。それぞれエスケープ文字で指定すればマッチ可能。
  • xは長文正規表現にコメントを埋め込むのに使われることが多いが、短い正規表現に空白区切りを追加する目的で使うほうがオススメ。
    • 区切りを可視化する程度しかできないが、短い正規表現ならそれで十分。
    • 運用レベルの話をすれば、空白区切りすら不要なレベルの単純な正規表現だけで済ます方が良い。
/^ [+-]? \d+ (_\d+)* (\.\d+ (_\d+)* )? ( [eE] [+-]? \d+ (_\d+)* )? $/x  # 浮動小数点数例
/^ [+-]? ( ((0d\d+ | [1-9]\d*) (_\d+)*) | 0+(_0+)*) $/x                 # 10進数例

特に覚える必要のない備忘録

  • 完全な備忘録として掲載はするが、これらが必要になった場合、設計そのものを見直すべき。ほとんどの場合それは単一の正規表現で解決するべきものではなく、もっと可読性とメンテナンス性の高いコーディングで解決するべきだからだ。正規表現は存在そのものがバギーだということを正しく認識し、使うとしても必要最低限にするべきだということを再度留意して欲しい。
.           # 再掲するがピリオドは使うべきではない マッチしても良い文字([:print:]など)だけを指定するべき
mxoオプション   # マルチラインマッチ、空白無視、評価の固定 RGSS3では使う意味も意義も無い
usenオプション  # 正規表現の文字コードを指定する RGSS3では基本的にUTF-8で変更する意味もない
/\cx|\C-x/  # Ctrl + x に対応する制御文字 (x は a から z のいずれか) ※古い記法で現在では使われない
/\M-x/      # Alt + x に対応するメタ文字 (x + 0x80 と等価)      ※古い記法で現在では使われない
/\M-\C-x/   # Alt + Ctrl + x に対応するメタ文字 (\C-x + 0x80 と等価) ※古い記法で現在では使われない
/[\1-\n]/   # 後方参照 マッチしたグループの内容にマッチする 特殊変数$1〜$nと同じ扱い
/(?#  )/    # 行内コメント 主にxオプションと併用する そもそもコメントがないと理解できない正規表現を書くべきではない
/(?=pat)/   # patの肯定先読み 基本的に使わなくても問題ない これが必要な複雑な正規表現は書くべきではない
/(?!pat)/   # patの否定先読み 基本的に使わなくても問題ない これが必要な複雑な正規表現は書くべきではない
/(?<=pat)/  # patの肯定後読み 基本的に使わなくても問題ない これが必要な複雑な正規表現は書くべきではない
/(?<!pat)/  # patの否定後読み 基本的に使わなくても問題ない これが必要な複雑な正規表現は書くべきではない
/(?>pat)n/  # アトミックグループ patにnが含まれる場合にnの手前での再マッチを試行しない=マッチに失敗する
/(?<name>)/ # 名前付きグループ 名前を付ける必要のある正規表現は書くべきではない グループは1〜2個に抑えるべき
/\k<name>)/ # 名前参照 数字指定で後方参照と同じ、負数指定で相対位置での後方参照も可能
/\g<name>/            # 部分式呼び出し       これが必要な複雑な正規表現は書くべきではない
/\k<[-+]n[-+]level>/  # ネストレベル指定マッチ参照 これが必要な複雑な正規表現は書くべきではない

日付時刻(Time)

  • Rubyでは日付と時刻はまとめてTimeオブジェクトで管理される。
  • 世界標準時と地方時の両方を扱えるが、基本的に地方時を扱うため特にロケールを気にする必要は無い。

Timeクラス

  • Timeオブジェクトは秒数で管理される。1妙未満の値はナノ秒整数もしくは小数で管理される。
    • 1分は60、1時間は3600、一日は86400、一年は31536000(365日)になる。
# クラスメソッド
Time.now               #=> 現在時刻を取得
Time.new               #=> 同上
Time.new(y,m,d,h,m,s)  # 日時時刻を与えてTimeオブジェクトを取得
                       # yearは省略できない 他を省略時はそれぞれの最小値となる
                       # 第7引数に標準時からの時差を指定可能だが通常使う必要は無い

# インスタンスメソッド
a = Time.new(2026,9,10,12,00,00)
b = Time.new(2026,9,10,12,01,00)
p a        #=> 2026-09-10 12:00:00 +0900
p a + 1    #=> 2026-09-10 12:00:01 +0900 右辺(Float)を秒数として加算したTimeオブジェクトを返す
p a - 1    #=> 2026-09-10 11:59:59 +0900 右辺(Float)を秒数として減算したTimeオブジェクトを返す
p a - b    #=> 1.0                       Timeオブジェクト同士の場合、秒数をFloatを返す
p a.year   #=> 2026 現在の年を返す
p a.month  #=> 9   現在の月を返す
p a.mon    #=> 9   同上
p a.day    #=> 10  現在の日を返す
p a.mday   #=> 10  同上
p a.hour   #=> 12  現在の時を返す    0〜23を返す
p a.min    #=> 0   現在の分を返す    0〜59を返す
p a.sec    #=> 0   現在の秒を返す    0〜60を返す(60はうるう秒らしい)
p a.usec   #=> 0   現在のマイクロ秒を整数で返す 精度はto_fよりも正確
p a.nsec   #=> 0   現在のナノ秒を整数で返す   精度はto_fよりも正確 実測値としてはマイクロ秒までしか計測されない
p a.to_i   #=> 1789009200  基準時からの経過秒数を整数で返す     1秒未満は含まない
p a.to_f   #=> 1789009200.0 基準時からの経過秒数を浮動小数点数で返す 1秒未満も含む
           # 基準時は 1970-01-01 00:00:00 UTC
p a.wday   #=> 4   現在の曜日を返す   0(日)〜6(土)
p a.yday   #=> 253  その年の通産日を返す 2月5日なら31+5=36
p a.sunday?     #=> false 日曜日ならtrue
p a.monday?     #=> false 月曜日ならtrue
p a.tuesday?    #=> false 火曜日ならtrue
p a.wednesday?  #=> false 水曜日ならtrue
p a.thursday?   #=> true  木曜日ならtrue
p a.friday?     #=> false 金曜日ならtrue
p a.saturday?   #=> false 土曜日ならtrue

# タイムゾーン関連
p a.zone        #=> "JST" タイムゾーン文字列を返す
p a.utc_offset  #=> 32400 タイムゾーン時差を秒数で返す(+09:00:00)
p a.gmt_offset  #=> 同上
p a.gmtoff      #=> 同上
p a.gmtime             # タイムゾーンを世界標準時に設定する
p a.utc                # 同上
p a.localtime          # タイムゾーンを現在のシステムの地方時に設定する
p a.localtime(offset)  # タイムゾーン時差をoffset秒数、もしくは"[+-]hh:mm"型式の文字列で設定する
p a.gmt?                  #=> false 世界標準時ならtrue
p a.utc?                  #=> 同上
p a.utc_offset == 32400   #=> true 地方時(日本標準時)ならtrue
getgm -> Time             # 世界標準時に変換したTimeオブジェクトを生成する
getutc -> Time            # 同上
getlocal -> Time          # 地方時に変換したTimeオブジェクトを生成する
getlocal(offset) -> Time  # 世界標準時 + offset秒数(または"[+-]hh:mm"型式の文字列)のTimeオブジェクトを生成する
参照: Ruby 1.9.3 リファレンスマニュアル > Time

拡張例

class Time
  def leap?   # うるう年ならtrue
    year % 4 == 0 && year % 100 != 0 || year % 400 == 0
  end
  def local?  # 現在の地方時ならtrue
    utc_offset == Time.now.utc_offset
  end
  def nendo   # 日本の場合の年度を返す 日本以外ならfiscal_yearなどを適切に定義する
    utc_offset == 32400 && mon < 4 ? year - 1 : year
  end
  alias format  :strftime  # 整形メソッドに分かりやすいエイリアス設定
  alias f  :strftime       # 短縮形
  alias %  :strftime       # Ruby2.6以降からの逆輸入
end
p Time.new(2024).leap?  #=> true  4の倍数年はtrue
p Time.new(2026).leap?  #=> false 4の倍数年でなければfalse
p Time.new(2100).leap?  #=> false 100の倍数年はfalse
p Time.new(2400).leap?  #=> true  400の倍数年はtrue
p Time.new(2026,3).nendo  #=> 2025 25年度を返す
p Time.new(2026,4).nendo  #=> 2026 26年度を返す 用途に応じて元号メソッドなども定義するのも良い

整形時刻

  • 整形メソッドを使ってTimeオブジェクトを整形できる。
  • Time#strftime(format) -> String
    • format文字列は下記参照。正直メソッド名がわかりづらいのでエイリアス推奨。エイリアスは format や f、% などもオススメ。
# 一般的なformat ここだけ覚えれば99%問題ない
p a % "%F"  #=> "2026-09-10" 日付(%Y-%m-%d)      ISO 8601の日付フォーマット
p a % "%T"  #=> "12:00:00"  24時間制の時刻(%H:%M:%S) ISO 8601の時刻フォーマット
p a % "%X"  #=> 同上

p a % "%Y"  #=> "2026" 西暦
p a % "%m"  #=> "09"  月(01-12) 1桁時は0埋め
p a % "%d"  #=> "10"  日(01-31) 1桁時は0埋め
p a % "%H"  #=> "12"  24時間制の時(00-23) 1桁時は0埋め
p a % "%M"  #=> "00"  分(00-59)
p a % "%S"  #=> "00"  秒(00-60) (60はうるう秒、使われるかどうかはシステムによる)

# 限定的なformat 基本的に覚えなくて良い
"%%"  # %自身
"%t"  # タブ文字 (\t)
"%n"  # 改行 (\n)

p a % "%C"  #=> "20"  世紀
p a % "%y"  #=> "26"  西暦の下2桁(01-99)

p a % "%e"  #=> "10"  日( 1-31) 1桁時は空白埋め
p a % "%k"  #=> "12" 24時間制の時( 0-23) 1桁時は空白埋め
p a % "%I"  #=> "12" 12時間制の時(01-12) 1桁時は0埋め
p a % "%l"  #=> "12" 12時間制の時( 0-12) 1桁時は空白埋め
p a % "%L"  #=> "000" ミリ秒 (000-999)
p a % "%N"  #=> "000000000" ナノ秒 "%6N"ならマイクロ秒 "%3N"ならミリ秒

p a % "%w"  #=> "4" 曜日(0-6) 日曜を0とする
p a % "%u"  #=> "4" 曜日(1-7) 月曜を1とする 日曜が7
p a % "%U"  #=> "36" 週(00-53) 最初の日曜日が第1週の始まり
p a % "%W"  #=> "36" 週(00-53) 最初の月曜日が第1週の始まり
p a % "%V"  #=> "37" 週(01-53) ISO 8601形式の暦週

p a % "%B"  #=> "September" 月の名称(January, February ... )
p a % "%b"  #=> "Sep"    月の省略名(Jan, Feb ... )
p a % "%h"  #=> 同上
p a % "%A"  #=> "Thursday"  曜日の名称(Sunday, Monday ... )
p a % "%a"  #=> "Thu"    曜日の省略名(Sun, Mon ... )
p a % "%P"  #=> "pm"     午前または午後(am,pm) フォーマット文字と変換文字が逆なので注意
p a % "%p"  #=> "PM"     午前または午後(AM,PM) フォーマット文字と変換文字が逆なので注意

p a % "%j"  #=> "253"     年中の通算日(001-366)
p a % "%s"  #=> "1789009200" 1970-01-01 00:00:00 UTC からの経過秒

p a % "%D"  #=> "09/10/26" 日付(%m/%d/%y)
p a % "%x"  #=> 同上
p a % "%c"  #=> "Thu Sep 10 12:00:00 2026" 日付と時刻(%a %b %m %H:%M:%S %Y)
p a % "%R"  #=> "12:00"    24時間制の時刻(%H:%M)
p a % "%r"  #=> "12:00:00 PM" 12時間制の時刻(%I:%M:%S %p)
p a % "%v"  #=> "10-SEP-2026" VMS形式の日付 (%e-%b-%Y)

p a % "%Z"  #=> "\x93\x8c\x8b\x9e (\x95W\x8f\x80\x8e\x9e)" タイムゾーン文字列 エンコードがおかしいので使わないこと
p a % "%z"  #=> "+0900" タイムゾーン時差

ファイル操作

  • Rubyのファイル操作は複数のクラスに分散して実装されているためやや面倒。ここだけオブジェクト指向が行き届いていない。
  • この項では目的別にサンプルコードを掲載する。
参照: Ruby 1.9.3 リファレンスマニュアル > Dir
参照: Ruby 1.9.3 リファレンスマニュアル > File
参照: Ruby 1.9.3 リファレンスマニュアル > FileTest

カレントディレクトリの取得

  • ファイル操作をする上で一番重要なのは、現在どのディレクトリにいるのかである。これが理解できてないと問題が起きる。


ファイル/ディレクトリの存在チェック

  • ファイルはともかく、ディレクトリは事前に存在チェックが必須。
    • ファイル作成時に存在しないファイルは自動的に作成されるが、存在しないディレクトリは作成されない。
Dir

**



**



**



**



Dirクラス拡張例

class Dir
  class << self
    alias current_path  :pwd  # カレントディレクトリパス取得クラスメソッドのエイリアス
    alias cpath  :pwd         # 同上
    alias path  :pwd          # 同上
    
    def root   # ルートディレクトリ取得クラスメソッド パス | nil を返す
      Dir.pwd[/^(.*)?\//, 1]
    end
    def up     # 一つ上のディレクトリパス取得クラスメソッド パス | nil を返す
      Dir.pwd.chomp("/")[/^(.*)\//, 1]
    end
    def rpath  # ルートディレクトリパス取得クラスメソッド パスを返す
      root or path
    end
    def upath  # 一つ上のディレクトリパス取得クラスメソッド パスを返す
      up or rpath
    end
  end
  
  def root   # ルートディレクトリ取得クラスメソッド パス | nil を返す
    path[/^(.*)?\//, 1]
  end
  def up     # 一つ上のディレクトリパス取得クラスメソッド パス | nil を返す
    path.chomp("/")[/^(.*)\//, 1]
  end
  def rpath  # ルートディレクトリパス取得クラスメソッド パスを返す
    root or path
  end
  def upath  # 一つ上のディレクトリパス取得クラスメソッド パスを返す
    up or rpath
  end
end

エンコード(Encoding) ※未稿

フリーエリア

管理人/副管理人のみ編集できます

広告募集中