Rails でObject#present?Object#blank?ArgumentError: invalid byte sequence in UTF-8が出て少し困ったのでその原因と対処法を残しておきます。

Ruby の String のエンコードは標準で UTF-8 になってるけども別のもの使えます。

> 'あ'.encoding
=> #<Encoding:UTF-8>
> str = 'あ'.encode('Shift_JIS', 'UTF-8')
=> "\x{82A0}"
> str.encoding
=> #<Encoding:Shift_JIS>
>"\x82\xA0".encode('UTF-8', 'Shift_JIS')
=> "あ"

ここで問題になるのはString#blank?が文字列の空白判定に正規表現を使っているため正しくエンコーディングが指定されていない文字列を渡すとArgumentError: invalid byte sequence in UTF-8が発生するわけです。

class String
  BLANK_RE = /\A[[:space:]]*\z/
  ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
    h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
  end
  def blank?
    empty? ||
      begin
        BLANK_RE.match?(self)
      rescue Encoding::CompatibilityError
        ENCODED_BLANKS[self.encoding].match?(self)
      end
  end
end

原因と対策

私の場合はunzipで ShiftJIS(Windows31J)の csv ファイルを展開した時にそのまま受け取ってしまってたのが原因だったのでiconvで UTF-8 に変換することで対策しました。

unzip_command = "unzip -p archive.zip data.csv"
+iconv_command = 'iconv -f WINDOWS-31J -t UTF-8'
-csv_string = `#{unzip_command}`
+csv_string = `#{unzip_command} | #{iconv_command}`

参考