金子邦彦研究室プログラミングRuby プログラミングRuby で CSV ファイルを扱う

Ruby で CSV ファイルを扱う

Ruby で CSV ファイルを扱うには,Ruby の CSV パッケージを使うのが簡単.ファイルサイズが巨大(例えば 100Mバイト以上)で無ければ性能的にも問題ない

このページでは,次のプログラム例を示す.

CSV ファイルを insert into 形式の SQL 文に変換するプログラムについては,別ページで説明

CSV ファイルの例 (an example CSV File)

次のような CSV ファイルを,ファイル名 hoge.csv で保存 (Store the following file. the file name is 'hoge.csv').

1, 2011, 12, 1, kaneko,   orange A, 1.2, 10, 2011-12-01 10:42:48
2, 2011, 12, 1, miyamoto, Apple M,  2.5, 2,  2011-12-01 10:42:59
3, 2011, 12, 3, kaneko,   orange B, 1.2, 8,  2011-12-03 11:08:56
4, 2011, 12, 4, miyamoto, Apple L,  3,   1,  2011-12-04 15:35:12

次のような CSV ファイルを,ファイル名 hoge2.csv で保存 (Store the following file. the file name is 'hoge2.csv').

1,100,1,2,3
2,200,hoge,hoge2,hoge3
3,300,"a","b","x"
4,400,"""foo","hoo2"""

次のような CSV ファイルを,ファイル名 hoge3.csv で保存 (Store the following file. the file name is 'hoge3.csv').

id, year, month, day, cname,    pname,    price, num, datetime
1,  2011, 12,    1,   kaneko,   orange A, 1.2, 10, 2011-12-01 10:42:48
2,  2011, 12,    1,   miyamoto, Apple M,  2.5, 2,  2011-12-01 10:42:59
3,  2011, 12,    3,   kaneko,   orange B, 1.2, 8,  2011-12-03 11:08:56
4,  2011, 12,    4,   miyamoto, Apple L,  3,   1,  2011-12-04 15:35:12

CSV ファイル読み込み例 (CSV file read example)

単純読み込み (simple read)

CVS ファイルの1行単位での読み込みでは、 CSV ファイルの各行が,文字列の配列になる (The unit of read is a 'line'. A line become an array).

require "csv"
CSV.open("hoge.csv", "r") do |row|
  p row
end

◆ 実行結果の例 (execution result example)

[image]

ヘッダと本体の読み込み (read header and body)

1行目がヘッダー.2行目以降が本体

require "csv"
reader = CSV.open("hoge3.csv", "r") 
header = reader.take(1)[0]
p header
p "=================================" 
reader.each do |row|
  p row
end

◆ 実行結果の例 (execution result example)

[image]

ヘッダと本体の読み込んで連想配列に格納 (read header and body, then store into an array)

1行目がヘッダー.2行目以降が本体

require "csv"
reader = CSV.open("hoge3.csv", "r") 
header = reader.take(1)[0]
T = Hash::new
header.each do |attr|
  T[attr.strip] = []
end
reader.each do |row|
  i = 0
  row.each do |item|
    T[header[i].strip].push(item.strip) 
    i = i + 1
  end
end

◆ 実行結果の例 (execution result example)

[image]

文字列が整数,浮動小数点数を表現しているかの判別(each filed data is numeric?)

#! ruby -Ks
# coding: windows-31j

require "csv"

def string_type(s)
  # + は一回以上、*は0回以上 ^ 行頭 $ 行末
  # 浮動小数点数  123.45, -123.45, -123.45 など
  if( ( s =~ /[-+]?([0-9]*\.[0-9]+$)/ ) == 0 )
    return "real"
  # 浮動小数点数  123.45e-06 など
  elsif( ( s =~ /[-+]?([0-9]*\.[0-9]+)e[-+][0-9]*$/ ) == 0 )
    return "real"
  # 浮動小数点数  123e-06 など
  elsif( ( s =~ /[-+]?([0-9]+)e[-+][0-9]*$/ ) == 0 )
    return "real"
  # 整数
  elsif( ( s =~ /[-+]?([0-9]+)$/ ) == 0 )
    return "integer"     
  end
end

if __FILE__ == $0
  CSV.open("hoge2.csv", "r") do |row|
    s = ""
    row.each do |field|
      s << field.strip
      type = string_type(field.strip) 
      if ( type != nil )
        s <<  ":"
        s <<  type
      end
      s <<  ","
    end
    puts s 
  end
end

◆ 実行結果の例 (execution result example)

[image]

文字列が「"」を含むときエスケープ (escape the character ")

カンマ区切りなのだけど CSV の流儀に従っていないという場合です. 例えば,「101, He said "Hello"」はカンマ区切りですが,CSV の流儀に合致しません. これは,「101, "He said ""Hello"""」のように変換する必要がある. ここでは「""」で1つの半角のダブルクオートを表すことを「エスケープ」と呼んでいます.

#! ruby -Ks
# coding: windows-31j

require 'pp'

def escape_double_quote(s)
  # 文字列が 「"」 (半角のダブルクオート)を含むとき
  # 「"」 を 「""」 に置換するとともに,
  # 文字列全体を「"」で囲む.これは CSV ファイルの流儀
  if ( /"/ =~ s )
    # 置換し,文字列全体を「"」で囲む
    return '"' + s.gsub('"','""') + '"'
  else
    # そのまま返す
    return s
  end
end

if __FILE__ == $0
  print escape_double_quote('hoge hoge hoge')
  print "\n"
  print escape_double_quote('hoge "hoge" hoge')
  print "\n"
end

◆ 実行結果の例 (execution result example)

[image]

CSV ファイルの生成 (Create CSV file)

■ 連番ファイルデータの生成

require "csv"

# open for write                                                                         
f = open("hoge2.csv", "w")
writer = CSV::Writer.generate(f)

# write                                                                                  
for i in 1..200
  writer << [i, " File" + sprintf("%06d", i) + ".png"]
end

f.close()
[image]

■ CSVファイルのデータを置換して、新しい CSV ファイルを生成 (Replace data in a CSV file and create a new CSV file)

CSV ファイルの 4, 5 列目を .gsub(/a/, "A") で置き換える。

require "csv"

# open for write
f = open("hoge2.csv", "w")
writer = CSV::Writer.generate(f)

# read and write                                                                         
CSV.open("hoge.csv", "r") do |row|
  # replace "a" to A inte column 4 and 5                                                 
  f0 = row[0]
  f1 = row[1]
  f2 = row[2]
  f3 = row[3]
  f4 = row[4].gsub(/a/, "A")
  f5 = row[5].gsub(/a/, "A")
  f6 = row[6]
  f7 = row[7]
  f8 = row[8]

  writer << [f0, f1, f2, f3, f4, f5, f6, f7, f8]
end

f.close()

◆ 実行結果の例 (execution result example)

[image]