morimorihogeです.更新が不定期ですみません.最近夕立が改二になりましたが3-4が越せません.(*1
普段Ruby/Railsを使っていると,基本的に文字コードはUTF-8を使っているかと思います.Rubyは割と文字コードについてはバージョン毎に扱いが変わっており,以下の様な挙動になります.
- 1.8.7以前: デフォルトUS-ASCII,日本語を扱う際は$KCODEの指定を明示的に呼び出す
- 1.9.3以前: デフォルトUS-ASCII,日本語を扱う際はcoding: utf-8等のマジックコメントを記述する
- 2.0.0以降: デフォルトUTF-8
というわけで,ここ最近のプロジェクトでRuby 2.0系を使うのであれば,マジックコメントも何も必要無くUTF-8を使うことができます.
しかし,レガシーシステムとの連携といった理由で他の文字コードのデータを読み書きする必要は依然存在します.本記事では試しにShiftJIS(CP932)のファイルを扱ってみます.動作確認はRuby 2.0.0p247で行っていますが,恐らく1.9.3系でも問題なく動作するかと思います.
非UTF-8ファイルの読み込み(文字列編)
UTF-8以外のファイルを文字列として読み込むときには,
File.read("hoge.txt", encoding: 'cp932')
とかやれば良いです.読み込み時に自動的にUTF-8に変換されて,その後は普通の文字列として扱えます.普通ならこれで良いですね.
非UTF-8ファイルの読み込み(バイナリ編)
さて,次はもうちょっと厄介なファイルを想定してみます.具体的にはファイルの区切り文字などがなく,バイト位置でデータ範囲が指定されている様な固定長データのケース(旧世代のCOBOLで書かれたえんたーぷらいずなシステムとかが好みそうな奴)を想定します.
このようなケースでは「XXバイト目からYYバイトはASDFというデータ」という固定長データを扱うことが多いです.今回処理する必要になったデータは,100以上のデータが毎行CRLFで区切られており,データの中にはSJIS以外の文字コードも許可されているというアレゲなファイルでした.
文字列がSJISとは限らないという問題の他に,データをバイト位置指定で読み込む必要があるため,今回は文字コード指定して読み込むのではなく,バイナリで読み込むことにしました.
Ruby 1.9.3以降でバイナリとしてファイルを読み込むにはFile.binreadを使い,以下の様に読み込みます.
binary_text = File.binread("hoge.txt")
その後,区切り文字はCRLFと分かっているので,splitにかけて1行ずつに分解,その後以下のTokenizerにかけてbytesliceにかけてparseしました.Ruby 1.9以降ではHashはordered hashになるので,FORMAT_HASHの順序には意味があります.また,bytesliceはRuby 1.9.3以降で使える様なので,古いRubyでは使えないことに注意して下さい.
とりあえず適当に読み込んでUTF-8変換して表示してみました.表示するときにはencodeメソッドで明示的に文字コード変換してやらないと文字化けるので注意です.
class HogeTokenizer
// 項目 => バイト数のハッシュ
FORMAT_HASH = {
'作成日' => 8,
'会員番号' => 8,
'会員ほげほげ区分' => 3,
'会員ふがふが区分' => 1,
'会員謎コード' => 11,
// ..... 仕様書に従って定義を書く
}.freeze
class << self
def parse_data(data)
current = 0
hash = {}
FORMAT_HASH.each do |key, length|
hash[key] = data.byteslice(current, length)
current += length
end
hash
end
end
end
binary_text = File.binread("hoge.txt")
data_array = binary_text.split("\r\n")
data_array.each do |data|
hash = HogeTokenizer::parse_data(data)
hash.each do |key, val|
puts " #{key}: #{val.encode('utf-8', 'cp932')}"
end
end
まとめ
というわけで,RubyでShiftJIS(CP932)のファイルを扱う方法をまとめてみました.普通はFile.readで済むのですが,稀にバイト列で扱いたいケースがあるので,そういった場合には参考になるかなと思います.
ではでは.
*1) 初稿時夕立改としていましたが,夕立改二の間違いです.lvl55は別府の70よりは楽でしたが3-2-1回すのはそろそろ苦痛になってきました