rubyベストプラクティスChapter7.1

入力が色々なエンコーディングが想定される場合のライブラリ側の対応方法。例としてCSVライブラリを見ている。
CSVライブラリは読み込みのデータが大量であることが結構あるので、入力データのエンコーディングを変更するのではなく、パーサ側のエンコーディングを変えてるらしい。
本の中で上記のことを実現するために

def sjis_re(str)
  Regexp.new(str.encode("Shift_JIS"))
end

ってメソッドを作っていて

"foo あ bar" =~ sjis_re("(\w+)\s\s(\w+)")

ってマッチを書いてるけどsjis_reに渡す時に「"」じゃ僕の環境ではマッチしなかった。

"foo あ bar" =~ sjis_re('(\w+)\sあ\s(\w+)')

ってするとマッチする。

これをcsvライブラリでは汎用的にしていてそれらが、escape_reとencode_reメソッドになる。

def escape_re(str)
  str.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join
end

def encode_re(*chunks)
  Regexp.new(encode_str(*chunks))
end

で、@re_charsと@re_escってのは

@re_esc = "\\".encode(@encoding) rescue ""
@re_chars =   %w[ \\ .  [  ]  -  ^  $  ?
                 *  +  {  }  (  )  |  #
                 \  \r \n \t \f \v ].
             map { |s| s.encode(@encoding) rescue nil }.compact

っていう風に設定されている。

よくわからんので、これらをirbでためしてみると

ruby1.9.2@180 > re_esc = "\\".encode(@encoding)
ruby1.9.2@180 > re_chars = %w[ \\ .  [  ]  -  ^  $  ? *  +  {  }  (  )  | \  \r \n \t \f \v ]
ruby1.9.2@180 > 'hoge'.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join
> hoge
ruby1.9.2@180 > 'ho\ge'.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join
> ho\\\\ge
ruby1.9.2@180 > Regexp.new('ho\ge'.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join)
> /ho\\ge/

ってなる。
本にも書いてある通り、正規表現上でのクオートしなきゃいけない文字列を適切にクオートしてくれるらしい。

ここでは、csvのような様々な文字コードの入出力を意識しなきゃいけないって時には内部のコードを入出力の文字コードにあわせるといいよってことだと思う。


Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック