第33回 Ruby/Rails 勉強会 - 演習 1

宿題の添削はしてくれるんだろうか?

演習 1

  • CSV ファイルを分割します。分割数 n を指定できます。
  • 読み込むファイル名を指定できます。
  • 出力先ディレクトリを指定できます。
  • 出力ファイル名のプレフィクスを指定できます。

なんだか力ずくでやってしまった。

#! /usr/local/bin/ruby19
# -*- coding: utf-8; -*-

require 'optparse'

# 指定されたオプションを覚える
option = {
  output: '.',  # 出力先
  prefix: '',   # prefix
  count:  2,    # 分割数
}

# オプションの指定
parser = OptionParser.new
parser.on('-d n',
          '--divide',
          'divide to n.'){|n| option[:count] = Integer(n)}
parser.on('-o dir',
          '--output-dir',
          'output directory.'){|o| option[:output] = o.sub(%r[/$], '')}
parser.on('-p prefix',
          '--prefix',
          'prefix of filename.'){|p| option[:prefix] = p}
parser.banner = <<USAGE         # csv ファイルを複数受け付けるよ
usage: #{File.basename $0} [options] [csv-file ...]
options:
USAGE

# コマンドライン引数の解析
begin
  parser.parse!(ARGV)
rescue Object =>e
  STDERR.puts "#{File.basename $0}: #{e.to_s}"
  STDERR.puts parser.help
  exit 1
end


# prefix の決定
#
# prefix を指定されなかったら basename を使う。
# ARGF から複数のファイルを受け取ったら、ファイルごとに変える。
# 標準入力なら 'stdin'
def guess_prefix(prefix, fn)
  prefix = File.basename(fn, ".*")  if prefix.empty?
  prefix = 'stdin'                  if prefix == '-'
  prefix
end

# 分割後のファイル名の Array
def output_files(count, dir, prefix)
  (0...count).map{|i| "%s/%s%03d.csv" % [dir, prefix, i]}
end

# csv を分割
#
# 各行を count 個に分割
def divide_csv(src, count, files)
  n = ((src.count(',')+1.0)/count).ceil  # ファイルごとのカラム数
  data = src.split(/\s*,\s*/).each_slice(n)
  files.each do |file|
    begin
      file.puts data.next.join(",")
    rescue StopIteration        # カラムが足らんがな
      file.puts
    end
  end
end


files = []                      # 分割後のファイル
pre = ""                        # 処理中の prefix

ARGF.each_line do |buf|
  tmp = guess_prefix(option[:prefix], ARGF.filename)
  # prefix が変わったら 再オープン
  if pre != tmp
    files.each{|f| f.close}
    files = []
    output_files(option[:count], option[:output], tmp).each do |fn|
      files << open(fn, "w")
    end
    pre = tmp
  end

  # 分割するぞ
  divide_csv(buf, option[:count], files)
end

# プロセス終了と共に files もクローズされる

OptionParser って、
-d n -o dir -p pre

-dop n dir pre
って書けないんだね。知らなかった。