Object#present?や#blank?でinvalid Byte Sequence in UTF 8になる時の対処法
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}`