• ベストアンサー

VBSでソート&ファイル分割

VBScriptでCSVファイルを最大5件のレコードになるように ファイル分割しようとしています。 ただし、同じコードが複数のファイルに分かれないようにしたいです。 入力するCSVファイル(test_in.csv)は以下のような形式です。 (実際のファイルに項目行はありません) 連番,コード,フラグ,日付 01,0001,A,20091001 02,0002,A,20091001 03,0003,A,20091002 04,0001,U,20091003 05,0003,D,20091003 06,0004,A,20091003 07,0005,A,20091003 08,0001,D,20091005 09,0006,A,20091003 10,0006,A,20091003 ※入力ファイルに同じコードのレコードが5件を超えることはありません) 上記の入力ファイルの場合は以下の3つのファイルに分割することになります。 【test_out_001.csv】 01,0001,A,20091001 04,0001,U,20091003 08,0001,D,20091005 02,0002,A,20091001 【test_out_002.csv】 03,0003,A,20091002 05,0003,D,20091003 06,0004,A,20091003 07,0005,A,20091003 【test_out_003.csv】 09,0006,A,20091003 10,0006,A,20091003 ※コード"0003"のレコードは2件あるので、test_out_001.csvには出力せず、  test_out_002.csvに出力します。  コード"0006"についても同様でtest_out_003.csvに出力します。 処理の手順としては 入力ファイルのデータをコードでソートし、 1ファイルに5件を超えないように追加していくのかと思いますが、 ソートと5件制限はどのように記述すればよいでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • nda23
  • ベストアンサー率54% (777/1415)
回答No.9

5件ずつ、しかもコードが生き別れにならない出力の例です。 Sub WriteData() Dim JIS, UTF, F, T, N, E, C, P F = 0 N = 0 Do Until f > CNT   Set JIS = CreateObject("ADODB.Stream")   JIS.Open   JIS.Type = 2   JIS.Charset = "shift_jis"   N = N + 1   P = "C:\ok\CSV\T" & Right("00" & CStr(N), 3) & ".csv"   E = F + 4   If E < CNT Then     C = BLK(E).Code     If C = BLK(E + 1).Code Then       Do         E = E - 1         If C <> BLK(E).Code Then Exit Do       Loop     End If   End If   Do     JIS.WriteText BLK(f).Seq & ","     JIS.WriteText BLK(f).Code & ","     JIS.WriteText BLK(f).Flag & ","     JIS.WriteText BLK(f).Date & vbNewLine     F = F + 1     if F > CNT Then Exit Do   Loop Until F > E   JIS.SetEOS   JIS.Position = 0   Set UTF = CreateObject("ADODB.Stream")   UTF.Open   UTF.Type = 2   UTF.Charset = "utf-16"   JIS.CopyTo UTF   JIS.Close   Set JIS = Nothing   UTF.SaveToFile P, 2   UTF.Close   Set UTF = Nothing Loop End Sub

superss28
質問者

お礼

分岐条件とループの脱出条件でここまで実現できるんですね…。 ご回答頂いたソースを実行してみて 実現したかった動作が確認できました。 後々、メンテナンスすることも考えられますので 一ステップごとに何の処理を行っているか理解しようと思います。 ご回答ありがとうございました。

その他の回答 (8)

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.8

並べ替えは以下のメソッドです。 Sub Sort(ByVal Fmi, ByVal Toi)   Dim F, T, D, M   F = Fmi   T = Toi   Set D = BLK(Fmi)   Do     Do While BLK(F).Comp(D) < 0       F = F + 1     Loop     Do While D.Comp(BLK(T)) < 0       T = T - 1     Loop     If F >= T Then Exit Do     Set M = BLK(T)     Set BLK(T) = BLK(F)     Set BLK(F) = M     F = F + 1     T = T - 1   Loop   F = F - 1   If F > Fmi Then Sort Fmi, F   T = T + 1   If T < Toi Then Sort T, Toi End Sub

  • kztk
  • ベストアンサー率53% (59/110)
回答No.7

ヘッダーなしだと1行目が項目名と解釈されてしまうのですが・・・ と聞かれそうな気がしたので、こちらもご参照のこと。 http://www.ken3.org/cgi-bin/test/test090-1.asp

superss28
質問者

お礼

ORDER句を使う方法でソースを作成し、 期待通りのソート結果を得ることが出来ました。 この方法ですと基本的なSQLされ知っていれば かなり応用することができますね。 ご回答ありがとうございました。

  • kztk
  • ベストアンサー率53% (59/110)
回答No.6

>"DBQ=D:\\vbs\\test0001.csv;" & _ 「\\test0001.csv」が余計です。TextDriverではフォルダを指定し、個々のcsvファイルが1つのテーブルのように扱われます。 >さらにこの方法で後々SQLを発行するはずですが、 >CSVには項目名がないのでORDER句が作れないような気がするのですが…。 select * from test0001.csv order by 2 でいけると思います。 だめだとしてもschema.iniを作ればいけます。 ちなみに、ずぼらな私はソート処理を自前で考えるのがイヤなのと、Text Driverだとソートだけでなく集計や抽出条件をつけることもできて応用がきくのでこちらをお勧めしていますが、nda23さんのご提示されている方法が真っ当でエレガントな気もしますので、ご検討ください。

superss28
質問者

お礼

ご回答ありがとうございます。 回答遅れて申し訳ないです。 せっかくですので両方の方法とも試してみたいと思います。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.5

SHIFT-JISに変換しますが、ファイルに保存する必要はありません。 読み込み処理は以下の通りです。 Sub ReadFile() Dim JIS, UTF, ARY, TXT, TMP Set JIS = CreateObject("ADODB.Stream") 'SHIFT-JIS側 JIS.Open JIS.Type = 2 'テキスト形式という意味 JIS.Charset = "shift_jis" Set UTF = CreateObject("ADODB.Stream") 'UTF-16側 UTF.Open UTF.Type = 2 UTF.Charset = "utf-16" UTF.LoadFromFile "C:\~\test_in.csv" '読み込み UTF.Position = 0 '先頭に位置付ける UTF.CopyTo JIS 'SHIFT-JISに変換(保存する必要は無い) JIS.SetEOS JIS.Position = 0 '先頭に位置付ける UTF.Close 'UTF-16用オブジェクトにはもう用は無い Set UTF = Nothing Do Until JIS.EOS '読み込みループ   TXT = "" '1行分のデータを初期化   Do Until JIS.EOS '改行かEOSまでのループ     TMP = JIS.ReadText(1) '1文字ずつ読み込む     If TMP = vbLf Then Exit Do '改行なら抜ける     TXT = TXT & TMP   Loop   ARY = Split(Replace(TXT, vbCr, ""), ",") '復帰を削除し、カンマで区切る   Set TMP = New REC '新しいデータの入れ物をインスタンス化する   TMP.Seq = ARY(0) '上記オブジェクトに記録(連番)   TMP.Code = ARY(1) '上記オブジェクトに記録(コード)   TMP.Flag = ARY(2) '上記オブジェクトに記録(フラグ)   TMP.Date = ARY(3) '上記オブジェクトに記録(日付)   CNT = CNT + 1 '配列要素数をインクリメント   ReDim Preserve BLK(CNT) '配列を拡張する   Set BLK(CNT) = TMP '配列の最後に追加する LOOP JIS.Close 'SHIFT-JIS用オブジェクトにはもう用は無い Set JIS = Nothing End Sub

superss28
質問者

お礼

ご回答ありがとうございます。 この方法だとファイルに保存する必要もなく 二次元配列のようなことができるんですね。 こちらの方法も試してみたいと思います。

  • kztk
  • ベストアンサー率53% (59/110)
回答No.4

>「■変換先のStreamを保存」 >まで試してみて変換先のファイルを開いてみたのですが、 >UTF-16BE形式になってしまいました。 ここ、 sto_in.SaveToFile "D:\vbs\test0001.csv",2 が、 sto_out.SaveToFile "D:\vbs\test0001.csv",2 です。せっかくSJISのストリームを作ったのに、元のUnicodeBEのストリームを保存しちゃだめです。

superss28
質問者

お礼

ご指摘ありがとうございます。その通りでした。 ADODB.Connectionを作成し、 以下の処理で変換先ファイルを開こうとしたのですが、 指定されたパスにファイルがないというエラーになってしまいました。 Dim objADOCon Set objADOCon = CreateObject("ADODB.Connection") objADOCon.Open "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _ "DBQ=D:\\vbs\\test0001.csv;" & _ "ReadOnly=0" さらにこの方法で後々SQLを発行するはずですが、 CSVには項目名がないのでORDER句が作れないような気がするのですが…。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.3

この問題は3個の処理に分割すべきです。 (1)元データの読み込み  UTF-16のデータを読み込んで、内部で処理できる形式に変換 (2)ソート  各レコードをソート (3)並べ替えデータの出力  最大5件、かつ同一データが生き別れにならない出力 試しにプログラムしたら131ステップになってしまいましたので、 これをソックリ記載するのは難しいため、触りの部分を記載します。 '**** レコードの入れ物となるオブジェクト **** Class REC Dim Seq '連番 Dim Code 'コード Dim Flag 'フラグ Dim Date '日付 Function Comp(Other) '比較関数(自身と引数) If Code > Other.Code Then   Comp = 1 '自分の方が大きい ElseIf Code< Other.Code Then   Comp = -1 '自分の方が小さい Else 'コードが同じ場合は連番で決める   If Seq > Other.Seq Then '自分の方が大きい     Comp = 1   ElseIf Seq < Other.Seq Then     Comp = -1 '自分の方が小さい   Else     Comp = 0 'コード、連番とも等しい   End If End If End Function End Class '****** ここからメイン・メソッド ****** ReDim BLK(0) 'レコード(RECオブジェクト)の配列 Dim CNT 'レコード数(実際は配列の最大インデックス値) CNT = -1 '配列の最大インデックス値なので、最初は-1 ReadData '(1)データの読み込み If CNT > 0 Then Sort 0, CNT '(2)ソート WriteData '(3)並べ替えたデータの書き込み '****** ここまでメイン・メソッド ****** 上記の例にはReadData、Sort、WriteDataのメソッドがありません。 メソッドの内容をご希望の場合は補足してください。

superss28
質問者

お礼

ご回答ありがとうございます。 >(1)元データの読み込み > UTF-16のデータを読み込んで、内部で処理できる形式に変換 この「内部で処理できる形式」というのはNO.2の回答者様のように いったんShift-JISに変換するのでしょうか…?

  • kztk
  • ベストアンサー率53% (59/110)
回答No.2

元ファイルがUTF-16BEであるためにいろいろな便利機能が使えないので、いったんShift-JISへ変換したらよいと思います。 変換にはADODB.StreamのCopyToが使えます。 http://msdn.microsoft.com/ja-jp/library/cc364138.asx ■元ファイルをStreamで読み込み。CharsetはUTF-16BE ■変換先のStreamを作成。CharsetはShift-JIS ■元ファイルのStreamから変換先のStreamへCoptyTo ■変換先のStreamを保存 ■変換後のCSVファイルに対してText DriverでSQL発行  : あと、先に書いたコードでは以下の要件を見逃していました。その辺はうまくやってください。 >ただし、同じコードが複数のファイルに分かれないようにしたいです。

superss28
質問者

お礼

ご回答ありがとうございます。 「■変換先のStreamを保存」 まで試してみて変換先のファイルを開いてみたのですが、 UTF-16BE形式になってしまいました。 ↓このような書き方をしたのですが間違ってますでしょうか? Dim sto_in Set sto_in = WScript.CreateObject("ADODB.Stream") sto_in.Charset = "UTF-16BE" sto_in.Open sto_in.Type = 2 sto_in.LoadFromFile("D:\vbs\test.csv") sto_in.LineSeparator = -1 sto_in.Position = 0 '変換先Stream作成 Dim sto_out Set sto_out = CreateObject("ADODB.Stream") sto_out.Charset = "Shift-JIS" sto_out.Open sto_in.CopyTo(sto_out) sto_in.SaveToFile "D:\vbs\test0001.csv",2 sto_in.Close Set sto_in = Nothing Set sto_out = Nothing

  • kztk
  • ベストアンサー率53% (59/110)
回答No.1

文字コードの制約はなくなったと考えてよいでしょうか。 入出力ともSJISでよい前提として、私ならばこんな感じでしょうか。 ■CSVの読み込みにはADOとODBC Text Driverを使用します。  VBS CSV Text Driver等で検索してみればサンプルが見つかります。   ■ソートはCSV読み込み時にSQLで指定するか、ADODB.Recordsetの  Sortプロパティがたぶん使えます。 ■5件づつの出力はただ単にループをまわしながら5件ごとに  出力ファイルを切り替えていくだけかと思います。   Dim oRst '結果を格納したRecordset Dim oOutFile '出力ファイル lCnt = 0 Do While Not oRst.EOF '5件ごとに出力ファイルを変える。 If lCnt Mod 5 = 0 Then Set oOutFile = FSO.CreateTextFile("test_out_" & Right("000" & lCnt \ 5 + 1, 3) & ".csv") End If 'ここの編集が格好悪いですが。。。 oOutFile.WriteLine oRst.Fields(1).Value & "," & ・・・・ lCnt = lCnt+1 oRst.MoveNext Loop

superss28
質問者

補足

ご回答ありがとうございます。 丁寧に回答して頂いたのですが、 入力ファイルの文字コードは"UTF-16BE"です…。 肝心なことが質問内容から漏れてしまいました。 大変申し訳ありません。

関連するQ&A

  • VBScriptでのファイル読込について

    CSVファイル内のデータをキー項目(コード)ごとに分け、 それぞれ別のCSVファイルに出力しようとしています。 使用する言語はVBSです。 CSVファイルは以下のような形式です。 連番,コード,フラグ,日付 1,0001,A,20091001 2,0002,A,20091001 3,0002,U,20091002 4,0001,D,20091003 CSVファイルは既定の文字コード(UnicodeBE BOMなし)の形式であり、 通常のファイルを読み込むような処理では正しく 読み込むことが出来ませんでした。 (OpenTextFileメソッドの第4引数にUnicodeBEは指定できないのでは…?) また、CSVファイル内のデータは"コード"でソートされておらず、 VBSではソート機能を標準で持っていないため そちらについても実装する必要があります。 ファイルを取り込む際の文字コードの指定や ソート処理について詳しい方、ご回答よろしくお願いします。

  • sortコマンドについて

    いつもお世話になっております。 sortコマンドについて質問させて下さい。 現在UNIXコマンドのsortによってファイルをソートしようとしています。 ソート対象となるファイルは下記の通りです。 ファイルの種類:csvファイル ファイルサイズ:80MByte 1レコード長:48Byte レコード数:120万レコード レコード例: 2,1000 ,1,10C0,15,0000000100000000000000000000000000000000 2,1117 ,1,10B0,16,0000000300000000000000000000000100000000 ・ ・ ・ そこで下記のようなコマンドにてソートしています。 sort -t, -k 1,1 -k 2,2 -k 3,3 -k 4,4 < fileA.csv > fileB.csv すると「sort:0653-657ソート中に書込みエラーが発生しました。」と出力され中断してしまいます。 自分なりにsortコマンドについて調べてみたところ、 /usr/tmpディレクトリのファイルシステムがいっぱいになることでコマンドが失敗しているようです。 (一時的にそのディレクトリにソート内容を展開しているようでした) 社内にて/usr/tmpディレクトリのファイルシステムの拡張を求めたところ「できない」と言われてしまいました。 開発の最終段階でのボリュームテストを行ったときの障害でしたので、 処理を大幅に変更するような対処は出来ない状況です。 代替方法などありましたらご教示頂きたいと思います。 以上、宜しくお願い致します。

  • 月名でソートを行いたいのですが・・・

    Solaris8上で、ソートコマンド『sort -M 入力ファイル名』をやったのですが、上手く月名でソートを行ってくれません。入力ファイルは、月名のみのレコードが書かれたテキストファイルです。以下に、私が入力した月名を示します。 JAN、FEB、MAR、APR、MAY、JUN、 JUL、AUG、SEP、OCT、NOV、DEC もう一点、同一レコードを削除する『-u』オプションも上手く動きません。 どなたかご存知でしたら教えて頂けませんか? 宜しくお願い致します。

  • csvファイルの読込みとソート

    いつも大変参考にさせていただいております。 csvファイルの読み込みとソートをしたく、ネットや過去ログ等を相当調べたのですが、完全に詰まってしまいました。 (検索キーワード:「php csv ソート」「php 二次元配列 ソート」など) とても困っています。どなたかよろしくお願いします。 以下のようなcsvファイルを読み込みソートしたいのです。 ■csvファイル 20110803, A, りんご 20111215, B, みかん 20110306, A, みかん 20110620, A, りんご 20110215, B, りんご ■個別にやりたい処理 (1)、左列の日付で昇順ソートしてすべて表示 (2)、「A」を含む行をすべて表示(日付順) (3)、「A」+「りんご」を含む行をすべて表示(日付順) □補足 csvをfgetcsvで読み込み、テーブルに入れて表示するところまではできました。 csvの行は増えていきます(max100行位)。列は固定。

    • ベストアンサー
    • PHP
  • 1レコードからなるファイルを複数レコードに分割

    可変長の1レコードのみのファイル(固定長のレコードが改行コードなくつながっている)をもとにして、固定長のcsvファイルを作成する方法をお教えください。 具体的には、 項目A(10バイト)、項目B(5バイト)、項目C(20バイト)からなる35バイトのデータが改行コードなくつながっている50000バイトくらいの1レコードを入力して、項目A,B,Cから1レコードが構成されるcsvファイルを作成したいと思います。 よろしくお願いします。

  • テキストファイルに改行コードを付加してレコードを分割したい。

    テキストファイルに改行コードを付加してレコードを分割したい。 MSDOSテキストファイルで、1レコードのサイズが128バイトのテキストファイルが あるのですが、レコード間に改行コードがありません。 レコード10件なら1280バイト 20件なら2560バイトのファイルです。 これを128バイトごとに改行コードを付加できるようなソフトはないでしょうか? ファイル分割ではなく、レコードの分割ができる物を探しています。 よろしくお願いします。

  • csvファイル内にてソートする方法

    ご協力お願いします。 あるログデータを取得したcsvファイルを作成しました。しかし、データ量も多く見やすいようにソートをかけたいのですが方法がわかりません。csvファイルの中身は以下のようになっています。 ___________________________ | 端末ID | ユーザーID | 日付 | 時間 | ――――――――――――――――――――――――― | ITD002 | 00000001 |2005/08/22| 11:00 | ――――――――――――――――――――――――― | ITD002 | 00000003 |2005/08/22| 21:00 | ――――――――――――――――――――――――― | ITD001 | 00000001 |2005/08/22| 12:00 | ――――――――――――――――――――――――― | ITD001 | 00000002 |2005/08/22| 18:20 | ――――――――――――――――――――――――― 以上のような中身になっています。レコード量は、もっと多いです。このランダムな順番に取得したレコードを 端末ID(昇順)ユーザーID(昇順)日付(降順)時間(降順)でソートする方法をご教授お願いします。

  • VBSでCSVのキー項目の取得

    VBSでCSVファイル(UnicodeBE BOMなし)内のデータを キー項目(コード)ごとに分け、 それぞれ別のCSVファイルに出力しようとしています。 (ファイル名にコードを付与することで別々にします。) しかし、キー項目の取得に失敗しているようで 分岐処理が正しく行われていません。 CSVファイル(test_in.csv)は以下のような形式です。 (実際のファイルに項目行はありません) 連番,コード,フラグ,日付 1,0001,A,20091001 2,0002,A,20091001 3,0002,U,20091002 4,0001,D,20091003 以前、こちらで質問し、ご回答頂いた内容を基に 処理を実装しました。 Option Explicit Dim iSt Dim tPath Dim oFso Dim stdOut Dim aLine Dim field Dim wk Set iSt = WScript.CreateObject("ADODB.Stream") iSt.Open iSt.Type = 2 'バイナリ iSt.Charset = "UTF-16BE" iSt.LoadFromFile("test_in.csv") iSt.LineSeparator = -1 'CRLF iSt.Position = 0 tPath = "E:\\test_out.csv" Set oFso = CreateObject("Scripting.FileSystemObject") Set stdOut = CreateTextFileBE(oFso,tPath) Do While Not iSt.EOS aLine = iSt.ReadText(-1) field = split(aLine,",") aLine = join(field,",") if field(1) = "0002" then WriteLine stdOut,aLine end if Loop iSt.Close oFso.Close Set iSt = Nothing Set oFso = Nothing WScript.Quit Function CreateTextFileBE(fso,Path) fso.CreateTextFile(Path).Write Chr(&HFE) & Chr(&HFF) Set CreateTextFileBE=fso.OpenTextFile(Path,8,,True) End Function Sub WriteLine(stdOut,Line) stdOut.Write Line & vbCrLf End Sub 0002のレコードが入ったCSVファイルを出力しようとしましたが、 レコードなしのCSVファイルが作成されてしまいます。 コードごとにファイルを分ける処理についてもよくわからず、 取りあえずコードを限定し1ファイルだけの出力にしています。

  • サイズの大きなテキストファイルのSORT

    3GB超のテキストファイルをSORTコマンドでソートしたいのですが、空(0KB)の出力ファイルが出来て処理が終わってしまいます。 数KB~数MBのテキストでは正常にソートされるのですが、GB単位になると上記の現象が発生します。 環境はWindowsNTServer4.0です。 ソートできるファイルサイズの上限、ディスクの空き容量等、制限があるのでしょうか? また、上記の現象を回避し、正常にソートする方法をご存知な方がいらっしゃいましたら、どうぞご教授ください。 宜しくお願い致します。 ↓ソートコマンドは至ってシンプルです。 sort /+1 < TEST.dat > OUT.txt

  • sortコマンドのオプションなどの意味

    シェルスクリプトに次のようなsortコマンドが出てきました。 sort -t, /export/home1/aaa -u -k 1 -o $CSV_OUTFILE/$OUTPUT_FILE \     $CSV_OUTFILE/$INPUT_FILE_AAA $CSV_OUTFILE/$INPUT_FILE_BBB (aaaとAAAとBBBは変えていますが、あとはまったくもとのままです。) 行の最後の\は行継続の意味でよろしいでしょうか。 -t の直後に "," (カンマ) がありますが、これは「(空白区切りではなくて)カンマ区切り」という意味でよろしいですか。 (-tとカンマの間に空白は要らないのでしょうか。カンマはシングルクォーテーションで囲まなくてよいのでしょうか。) /export/home1/aaa というディレクトリの指定がありますが、これはなんのためでしょうか。 -u は同一行は重ねて出力しないという意味でよろしいでしょうか。 (キーが同じ行は重ねて出力しないという意味ではないですよね?) -k 1 の意味がわかりません。(ソートするキーの指定?) インプットファイルの指定と思われるものが2つありますが、入力ファイルは複数でも構わないのでしょうか? 以上ですが、わかるものだけでも結構です。 よろしくお願いします。