CSVファイルを読み込んで、入力データをそのまま出力しますが、重複データに対して、最後に「重複」という文字をつけるスクリプトです。
基本的には、「3-2-1.キーの重複したデータを1つにする」と「3-2-2.キーが重複したデータだけを出力する」を組み合わせ、それぞれの出力ファイルをマージ処理する方法を採っています。
実際に応用する場合は、入出力データのファイル名などを変更してください。また、入力データはキー順に並べ替えてあることが前提となっています。Rubyを利用して並べ替えを行う場合については、[2-4.ソート(並べ替え)処理]を参照してください。
なお、同様の処理を行う[3-2-6.ハッシュを使って、キーの重複したデータにマークをつけて、出力する]については、入力データをキー順に並べ替えておく必要はありません。
また、スクリプトで「重複」という漢字を指定していますので、Ruby1.9以降で使用するものとして、マジックコメントを1行目に指定しています。マジックコメントについては[2-1-5.スクリプトの中で漢字を使う]を参照してください。Ruby1.8以前であれば、$KCODEを使用します。
# coding:windows-31j
# double2.rb
# 内容 : 重複チェック後、重複データには、「重複」と表示する。
# 前提 : 重複チェックしたいキーであらかじめソートしておく。
# Copyright (c) 2002-2015 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>ruby double2.rb
#
# 入力ファイル
in11_file = open("double.txt","r")
# 出力ファイル(重複データを除いたレコード)
out11_file = open("single_temp.txt","w")
# 初期値設定
in1 = [] #入力データ
in1_key = nil #入力キー
sv_key = nil #1件前の保存キー
# 主処理
while (line1 = in11_file.gets)
line1.chomp!
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
#キー項目が単独のとき
in1_key = in1[0]
#キー項目が複数のとき
#1番目と2番目と3番目の項目がキーとなる場合
# in1_key = in1[0] + in1[1] + in1[2]
if ((sv_key != in1_key) &&
(sv_key != nil))
out11_file.print in1.join(","),"\n"
end
sv_key = in1_key
end
# ファイルのクローズ
in11_file.close
out11_file.close
#-------------------------------------------------
# 入力ファイル
in21_file = open("double.txt","r")
# 出力ファイル(重複データのみレコード)
out21_file = open("double_temp.txt","w")
# 初期値設定
in1 = [] #入力データ
in1_key = nil #入力キー
in1_ctr = 0 #入力件数
sv_key = nil #1件前の保存キー
sv_key2 = nil #2件前の保存キー
save1 = [] #保存データ
# 主処理
while (line1 = in21_file.gets)
line1.chomp!
in1_ctr = in1_ctr + 1
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
#キー項目が単独のとき
in1_key = in1[0]
#キー項目が複数のとき
#1番目と2番目と3番目の項目がキーとなる場合
# in1_key = in1[0] + in1[1] + in1[2]
if (sv_key == in1_key)
save1 = save1.push ["重複"]
out21_file.print save1.join(","),"\n"
sv_key2 = sv_key
elsif ((sv_key2 == sv_key) &&
(sv_key != nil))
save1 = save1.push ["重複"]
out21_file.print save1.join(","),"\n"
end
sv_key = in1_key
save1 = in1
end
if ((in1_ctr > 0) &&
(sv_key2 == sv_key))
save1 = save1.push ["重複"]
out21_file.print save1.join(","),"\n"
end
# ファイルのクローズ
in21_file.close
out21_file.close
#-------------------------------------------------
# 入力ファイル(重複データのみレコード)
in31_file = open("double_temp.txt","r")
# 入力ファイル(重複データを除いたレコード)
in32_file = open("single_temp.txt","r")
# 出力ファイル(マージファイル)
out31_file = open("double_check.txt","w")
# 初期値設定
in1_eof = ["ffffffff"].pack("h8"); #マスターファイルの終了判定
in2_eof = ["ffffffff"].pack("h8"); #トランザクションファイルの終了判定
in1 = [] #マスターデータ
in1_key = nil #マスターキー
in2 = [] #トランザクションデータ
in2_key = nil #トランザクションキー
#
# マスタファイル入力
#
class S_in1
def dataset(in31_file,in1_eof)
if (line1 = in31_file.gets)
line1.chomp!
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
#キー項目が単独のとき
in1_key = in1[0]
#キー項目が複数のとき
#1番目と2番目と3番目の項目がキーとなる場合
# in1_key = in1[0] + in1[1] + in1[2]
#マスターファイルが終了のとき
else
in1 = nil
#終了判定をセット
in1_key = in1_eof
end
return in1,in1_key
end
end
#
# トランザクションファイル入力
#
class S_in2
def dataset(in32_file,in2_eof)
if (line2 = in32_file.gets)
line2.chomp!
in2 = (line2 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
#キー項目が単独のとき
in2_key = in2[0]
#キー項目が複数のとき
#1番目と2番目と3番目の項目がキーとなる場合
# in2_key = in2[0] + in2[1] + in2[2]
#トランザクションファイルが終了のとき
else
in2 = nil
#終了判定をセット
in2_key = in2_eof
end
return in2,in2_key
end
end
#
# マッチングしたときの処理
#
def s_match(in1,in2,out31_file)
out31_file.print in1.join(","),"\n"
end
#
# マスターオンリーの処理
#
def s_master_only(in1,out31_file)
out31_file.print in1.join(","),"\n"
end
#
# トランザクションオンリーの処理
#
def s_trans_only(in2,out31_file)
out31_file.print in2.join(","),"\n"
end
# マスタファイル1件目入力
s_in1 = S_in1.new
(in1,in1_key) = s_in1.dataset(in31_file,in1_eof)
# トランザクションファイル1件目入力
s_in2 = S_in2.new
(in2,in2_key) = s_in2.dataset(in32_file,in2_eof)
#
# 入力データが終了するまで繰り返す。
#
until ((in1_key == in1_eof) &&
(in2_key == in2_eof))
#
# 主処理
#
#マッチング処理
if (in1_key == in2_key)
until (in1_key != in2_key)
s_match(in1,in2,out31_file)
(in2,in2_key) = s_in2.dataset(in32_file,in2_eof)
end
(in1,in1_key) = s_in1.dataset(in31_file,in1_eof)
#マスターオンリー処理
elsif (in1_key < in2_key)
s_master_only(in1,out31_file)
(in1,in1_key) = s_in1.dataset(in31_file,in1_eof)
#トランザクションオンリー処理
elsif (in1_key > in2_key)
s_trans_only(in2,out31_file)
(in2,in2_key) = s_in2.dataset(in32_file,in2_eof)
end
end
# ファイルのクローズ
in31_file.close
in32_file.close
out31_file.close
スクリプトはキーが重複しないように出力したレコードと、重複したレコードをマッチング(照合)することで、最初の入力データに対して、重複したレコードに「重複」の文字を表示するようにしたものです。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
1.入力データがタブ区切りの場合
上記のスクリプトはCSV2形式以外の引用符(")を使用する場合を含め、あらゆるCSVファイルに対応できるようになっていますが、入力データがタブ区切りの場合は
line1.chomp!
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
となっている箇所を
in1 = line1.split("\t",-1);
と変更します。
2.キーが複数の項目から成り立っている場合
「in1_key = in1[0]」の部分を
in1_key = in1[0] + in1[1] + in1[2]
のように変更します(文字列の連結は「+」を使う)。
aaa,1 aaa,2 aaa,3 bbb,1 ccc,1 ccc,2 ddd,1 ddd,2 ddd,3 ddd,4 eee,1 fff,1 ggg,1 hhh,1 hhh,2 hhh,3
aaa,1,重複 aaa,2,重複 aaa,3,重複 bbb,1 ccc,1,重複 ccc,2,重複 ddd,1,重複 ddd,2,重複 ddd,3,重複 ddd,4,重複 eee,1 fff,1 ggg,1 hhh,1,重複 hhh,2,重複 hhh,3,重複