rubyでurlのencode

URI.encodeだっけCGI.escapeだっけ、そういえばuってaliasなかったけとなったので
ソース見てみました。

結論

こちらで議論されているように、CGI.escapeとERB::Util.uでは挙動が異なります。

ruby -r cgi -r erb -e 'puts CGI.escape("a b"), ERB::Util.u("a b")'

$ ruby -r cgi -r erb -e 'puts CGI.escape("a b"), ERB::Util.u("a b")'
a+b
a%20b


基本は、ERB::Util.uでいいのかな。(Railsのviewで使うならuでok)
CGI.escapeの方が早いらしいのでこの差を意識しない場合のみCGI.escapeでしょうか。


ソース抜粋

URI.encode

使っちゃダメです。
obsoleteでした。
#1.9.2/lib/ruby/1.9.1/uri/common.rb

  module Escape
    def escape(*arg)
      warn "#{caller(1)[0]}: warning: URI.escape is obsolete" if $VERBOSE
      DEFAULT_PARSER.escape(*arg)
    end
    alias encode escape
  end
  extend Escape

関係ないけど、ソースのインデントにtabとspace併用してて気持ち悪い。


るりまサーチで調べると
singleton method URI.encode

このメソッドは obsolete です。

代わりに ERB::Util.url_encode, CGI.escape, URI.encode_www_form_component, WEBrick::HTTPUtils.escape_form, WEBrick::HTTPUtils.escape などの使用を検討してください。 詳細は [ruby-core:29293] からのスレッドを参照してください。

なんかいろいろあるのね。

ERB::Util.url_encode

#1.9.2/lib/ruby/1.9.1/erb.rb

class ERB
  module Util
    def url_encode(s)
      s.to_s.dup.force_encoding("ASCII-8BIT").gsub(/[^a-zA-Z0-9_\-.]/n) {
        sprintf("%%%02X", $&.unpack("C")[0])
      }
    end
    alias u url_encode
    module_function :u
    module_function :url_encode
  end
end
CGI.escape

#1.9.2/lib/ruby/1.9.1/cgi/util.rb

class CGI
  def CGI::escape(string)
    string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
      '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
    end.tr(' ', '+')
  end
end

正規表現での空白の扱いが異なります。
最後で空白を+に置換してますね。


あと、unpackの仕方が違うようですが、いまいちよくわかりません。
C:unsigned char (8bit 符号なし整数)
H2:16進文字列

おまけ WEBrick::HTTPUtils.escape_form

#1.9.2/lib/ruby/1.9.1/ebrick/httputils.rb

module WEBrick
  module HTTPUtils

    reserved = ';/?:@&=+$,'
    control  = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
    space    = " "
    delims   = '<>#%"'    
    unwise   = '{}|\\^[]`'
    nonascii = (0x80..0xff).collect{|c| c.chr }.join 

    def _make_regex(str) /([#{Regexp.escape(str)}])/n end
    def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1.ord } end

    UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
    UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)

    def escape(str)
      _escape(str, UNESCAPED)
    end

    def escape_form(str)
      ret = _escape(str, UNESCAPED_FORM)
      ret.gsub!(/ /, "+")
      ret
    end
  end
end

関係ないけど

CGI.escapeってなんでencodeじゃないんだろう。
html_escapeと混同すると思うんやけど。