LoginSignup
0
2

More than 5 years have passed since last update.

IO.read/readlines をNKF対応させる もしくは、binding.pryでソースが表示されずにキレた話

Posted at

before_session hook failed: ArgumentError: invalid byte sequence in Windows-31J

これにイラっときた人は少なくない筈
非Windows環境ならば、UTF-8で統一すれば大分減るはずですが

事の発端

binding.pry の直前に日本語コメントが入ってて表示できなかった
何度もこの問題(default_external と別エンコードのファイルの読み込みに失敗する)に直面して
正直疲れたので、いっそハックしてしまおう

対策

.pryrc
require 'nkf'
# Fix IO
module IO_NKF
  def read(path, length = nil, offset = 0, opts = {})
    keys = opts.keys
    return super unless (keys & %i[encoding external_encoding binmode]).empty?
    return super if keys.include?(:mode) && opts[:mode].include?(':')
    NKF_read path, length, offset, opts
  end

  def NKF_read(path, length = nil, offset = 0, opts = {})
    len = length || 1024
    enc = NKF_guess path, [1024, len].min, offset
    read path, length, offset, NKF_opts(enc, opts)
  end

  def readlines(path, rs, limit, opts={})
    keys = opts.keys
    return super unless (keys & %i[encoding external_encoding binmode]).empty?
    return super if keys.include?(:mode) && opts[:mode].include?(':')
    NKF_readlines(path, rs, limit, opts)
  end

  def readlines(path, rs = $/, opts = {})
    keys = opts.keys
    return super unless (keys & %i[encoding external_encoding binmode]).empty?
    return super if keys.include?(:mode) && opts[:mode].include?(':')
    NKF_readlines(path, rs, opts)
  end

  def NKF_readlines(path, rs, limit, opts)
    bin = binread(path, [1024, limit].min)
    encode = NKF.guess bin
    readlines path, rs, limit, NKF_opts(encode, opts)
  end

  def NKF_readlines(path, rs = $/, opts = {})
    enc = NKF_guess path
    readlines path, rs, NKF_opts(enc, opts)
  end

  def NKF_guess(path, limit = 1024, offset = 0)
    encode = NKF.guess binread(path, limit, offset)
    case encode
    when NKF::JIS then nil
    when NKF::EUC then Encoding::EUCJP
    when NKF::SJIS then Encoding::CP932
    when NKF::UNKNOWN then nil
    when NKF::UTF8 then Encoding::UTF_8
    when NKF::UTF16 then Encoding::UTF_16
    end
  end

  def NKF_opts(enc, opts = {})
    opts.dup.tap{|o|
      o[:external_encoding] = enc
    }
  end
end

# クラスメソッドをprependする方法ってこれでいいのかな
IO.singleton_class.class_eval { self.prepend(IO_NKF) }
Encoding.default_internal ||= Encoding::UTF_8

コレをpryrcに仕込めば、コードの文字コードに左右されにくくなります

後でGem化してみるかも

既知の問題点

  • JISと判別された場合どうすりゃいいんだろう
  • BOM 非対応
  • UTF32 非対応

最後に

File.read(path) を外部ファイルの読み込みに使ってる人は爆発しろエンコーディングを設定する必要がないのかきちんと考えてください

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2