🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

VB

VB(ビジュアルベーシック)はマイクロソフトによってつくられたオブジェクト指向プログラミング言語のひとつで、同社のQuickBASICが拡張されたものです。VB6の進化版といわれています。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

VBScript

VBScript(Visual Basic Scripting Edition)はMicrosftが開発したスクリプト言語であり、Visual Basicのサブセットです。

Q&A

解決済

4回答

4167閲覧

vbs ファイルの差分比較

hj_zebra

総合スコア10

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

VB

VB(ビジュアルベーシック)はマイクロソフトによってつくられたオブジェクト指向プログラミング言語のひとつで、同社のQuickBASICが拡張されたものです。VB6の進化版といわれています。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

VBScript

VBScript(Visual Basic Scripting Edition)はMicrosftが開発したスクリプト言語であり、Visual Basicのサブセットです。

0グッド

0クリップ

投稿2019/10/10 14:39

編集2019/10/11 01:56

【質問内容】
test1.csvの社員番号と日付とtest2.csvの番号と日付が一致した時だけ
test1.csvの時刻とtest2.csvの3つの時刻の中で(計4つ)の中で一番時刻の遅い値を返すプログラムを書こうとしています。
test2.csvにデータがなかった場合はそのままtest.1の内容を出力したいです。

for文などを使用して添え字やキーなどを作れば可能になりますでしょうか。
ご教示ください。

###テストデータ

**テストファイル1:test1.csvの内容** 12345,2019/01/01,17:25 12345,2019/01/02,19:25 12345,2019/01/03,21:25 12345,2019/01/04,23:25
**テストファイル2:test2.csvの内容** 12345,2019/01/01,17:25,23:25,19:25 12345,2019/01/02,19:25,20:25,21:25 12345,2019/01/03,21:25,19:25,15:25 12345,2019/01/04,23:25,21:25,21:25
**出力したい内容:OutFile1.csv** 12345,2019/01/01,23:25 12345,2019/01/02,21:25 12345,2019/01/03,21:25 12345,2019/01/04,23:25

発生している問題・エラーメッセージ

内側のDo Until objInFile2.AtEndOfStreamが終わったあと
test1.csvの2行目を比較したいのですが、書き込み処理に移ってしまいます。

■現在出力される内容
12345,2019/01/01,23:25
12345,2019/01/02,19:25
12345,2019/01/03,21:25
12345,2019/01/04,23:25

### 作成を試みたソース

Const Path = "C:" Const InFile1 = "test1.csv" Const InFile2 = "test2.csv" Const OutFile1 = "OutFile1.csv" Dim objFso Dim T_KEY Dim SYA_BG Dim KINMU_DATE Dim TIME_1_1 Dim M_KEY Dim SYA_BG2 Dim KINMU_DATE2 Dim TIME2_1 Dim TIME2_2 Dim TIME2_3 Dim count1 Dim count2 Dim NUM(4) Dim NUM_MAX Set objFso = CreateObject("Scripting.FileSystemObject") Set objInFile = objFso.OpenTextFile(Path & "\" & InFile1, 1, False) Set objInFile2 = objFso.OpenTextFile(Path & "\" & InFile2, 1, False) Set objOutFile = objFso.OpenTextFile(Path & "\" & OutFile1, 2, True) Do Until objInFile.AtEndOfStream strInFileLine = Split(objInFile.ReadLine, ",") SYA_BG = strInFileLine(0) KINMU_DATE = strInFileLine(1) TIME_1_1 = strInFileLine(2) Do Until objInFile2.AtEndOfStream strInFileLine2 = Split(objInFile2.ReadLine, ",") SYA_BG2 = strInFileLine2(0) KINMU_DATE2 = strInFileLine2(1) NUM(1) = TIME_1_1 NUM(2) = strInFileLine2(2) NUM(3) = strInFileLine2(3) NUM(4) = strInFileLine2(4) IF SYA_BG = SYA_BG2 AND KINMU_DATE = KINMU_DATE2 Then for i = 1 to 4 if NUM_MAX < NUM(i) Then NUM_MAX = NUM(i) END IF NEXT END IF TIME_1_1 = NUM_MAX Loop objOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & TIME_1_1 Loop objInfile.Close objInfile2.Close objOutfile.Close Set objInfile = Nothing Set objInfile2 = Nothing Set objOutFile1 = Nothing Set objfso = Nothing

使用言語

VBS

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Youbun

2019/10/11 00:38

https://teratail.com/help#about-template このサイトの「対応しているMarkdownの記法を知りたい」項目をみて ソースコードを貼り付けてください。 また、ソースコードのインデントもきっちりしてくれると ソースコードが見やすくなって良い感じになると思います。
YAmaGNZ

2019/10/11 00:57

内側のループの中にobjOutFile.WriteLineがあるので、内側のループで出力するのはあたりまえなのでは?
Y.H.

2019/10/11 00:57

> 書き込み処理に移ってしまいます 「書き込み処理」とは提示のソースのどの部分(行)でしょうか? 質問に記載ください。
Y.H.

2019/10/11 00:58

タグに[java]がありますが、どのように関係するのでしょうか?質問に記載ください。 もし関係しないのであれば削除ください。
hj_zebra

2019/10/11 01:57

>YAmaGNZさん 回答ありがとうございます。 外側にしたところ出力したい内容に近づきました
hj_zebra

2019/10/11 01:59

>Y.H.さん 書き込み処理についてはobjOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & TIME_1_1 になります。 不必要なタグは削除いたしました。
guest

回答4

0

ベストアンサー

条件に一致して処理したあと、内側のループを抜けていないからではないでしょうか。
If文の最後でExit Doすればよろしいかと。
それとobjInFile2が継続して処理されてしまうので、外側のループの最初でオープン、終了でクローズしたほうがよいでしょう。
本来なら開きっぱなしでファイルポインタだけ先頭に移動できればよいのですが、TextStreamでやる方法がわかりませんでした。
以上のことを考慮して修正したコードです。
修正したところは★でコメント入れてあります。
動作未確認ですので何かあればコメントください。

VBA

1Const Path = "C:" 2Const InFile1 = "test1.csv" 3Const InFile2 = "test2.csv" 4Const OutFile1 = "OutFile1.csv" 5 6 7Dim objFso 8Dim T_KEY 9Dim SYA_BG 10Dim KINMU_DATE 11Dim TIME_1_1 12 13Dim M_KEY 14Dim SYA_BG2 15Dim KINMU_DATE2 16Dim TIME2_1 17Dim TIME2_2 18Dim TIME2_3 19 20 21Dim count1 22Dim count2 23Dim NUM(4) 24Dim NUM_MAX 25 26Set objFso = CreateObject("Scripting.FileSystemObject") 27Set objInFile = objFso.OpenTextFile(Path & "\" & InFile1, 1, False) 28Set objOutFile = objFso.OpenTextFile(Path & "\" & OutFile1, 2, True) 29Do Until objInFile.AtEndOfStream 30 strInFileLine = Split(objInFile.ReadLine, ",") 31 SYA_BG = strInFileLine(0) 32 KINMU_DATE = strInFileLine(1) 33 TIME_1_1 = strInFileLine(2) 34 NUM_MAX = TIME_1_1 ' ★追加 35 Set objInFile2 = objFso.OpenTextFile(Path & "\" & InFile2, 1, False) ' ★ここに移動 36 Do Until objInFile2.AtEndOfStream 37 strInFileLine2 = Split(objInFile2.ReadLine, ",") 38 SYA_BG2 = strInFileLine2(0) 39 KINMU_DATE2 = strInFileLine2(1) 40 IF SYA_BG = SYA_BG2 AND KINMU_DATE = KINMU_DATE2 Then 41 for i = 2 to 4 ' ★2からに変更 42 if NUM_MAX < strInFileLine2(i) Then ' ★配列を直接参照 43 NUM_MAX = strInFileLine2(i) 44 END IF 45 NEXT 46 Exit Do ' ★追加 47 END IF 48 'TIME_1_1 = NUM_MAX ' ★これは不要 49 Loop 50 objInfile2.Close ' ★追加 51' objOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & TIME_1_1 ' ★削除して 52 objOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & NUM_MAX ' ★これに変更 53Loop 54objInfile.Close 55objOutfile.Close 56Set objInfile = Nothing 57Set objInfile2 = Nothing 58Set objOutFile1 = Nothing 59 60Set objfso = Nothing 61

投稿2019/10/11 02:33

編集2019/10/11 03:08
ttyp03

総合スコア17000

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YAmaGNZ

2019/10/11 02:44

objOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & NUM_MAX なら分かりますが、不要と削除した TIME_1_1 = NUM_MAX がないと、せっかく求めた一番遅い時間が更新されないのではないですか?
ttyp03

2019/10/11 02:55

あ、ホントだ。 見落としてました。 なんで折角求めたNUM_MAXを使わないで、TIME_1_1を使ってるんだ・・・。 指摘ありがとうございます。 あとで修正しておきます。
ttyp03

2019/10/11 02:59

というか、NUM_MAXの初期化もありませんね。
hj_zebra

2019/10/11 04:03 編集

>ttyp03さん とても分かりやすく回答していただきありがとうございました。 おかげで解決できました。
guest

0

単純にtest2.csvのほうが1回目のループで最後まで読み込まれ、次の外側のループでまったく処理されていないのが原因かと思います。

VBS

1 2 Set objInFile2 = objFso.OpenTextFile(Path & "\" & InFile2, 1, False) 3 Do Until objInFile2.AtEndOfStream 4 '処理 5 Loop 6 objInFile2.Close 7

とループごとに閉じて最初から読み込むようにすればよろしいかと思います。

最終時刻を求める際に

VBS

1Do Until objInFile.AtEndOfStream 2 strInFileLine = Split(objInFile.ReadLine, ",") 3 SYA_BG = strInFileLine(0) 4 KINMU_DATE = strInFileLine(1) 5 6 NUM_MAX = strInFileLine(2) '比較対象として初期化 7 8 Set objInFile2 = objFso.OpenTextFile(Path & "\" & InFile2, 1, False) 9 Do Until objInFile2.AtEndOfStream 10 strInFileLine2 = Split(objInFile2.ReadLine, ",") 11 SYA_BG2 = strInFileLine2(0) 12 KINMU_DATE2 = strInFileLine2(1) 13 NUM(1) = strInFileLine2(2) 14 NUM(2) = strInFileLine2(3) 15 NUM(3) = strInFileLine2(4) 16 IF SYA_BG = SYA_BG2 AND KINMU_DATE = KINMU_DATE2 Then 17 for i = 1 to 3 18 if NUM_MAX < NUM(i) Then 19 NUM_MAX = NUM(i) 20 END IF 21 NEXT 22 END IF 23 'TIME_1_1 = NUM_MAX '不要 24 Loop 25 objInFile2.Close 26 objOutFile.WriteLine SYA_BG & "," & KINMU_DATE & "," & NUM_MAX 27Loop

と比較対象の初期化を行うほうがよろしいかと思います。

投稿2019/10/11 02:30

編集2019/10/11 03:48
YAmaGNZ

総合スコア10469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hj_zebra

2019/10/11 04:03

>YAmaGNZさん とても分かりやすく回答していただきありがとうございました。 おかげで解決できました。
guest

0

内側のループでファイル(test2.csv)をAtEndOfStreamまで読んでいますが
これによりFSO内部の読み込みポインタがファイル終端になっていますので
2回目は読み込めません。以降内側のAtEndOfStreamは常にTrueとなります。

このためobjInFile(外側)の1ループ目はうまくいきますが、2ループ目
以降は内側のAtEndOfStreamが最初からTrueとなり内側のループには入
りません。

対策としては、以下が考えられます。
(a) test2.csvを毎回オープン/クローズする。
(b) test2.csv全体をStringに保存し、毎回そこから切り出す
(c) CSVファイルをExcelテーブルに読み込むんで処理

…オススメは(c)かな。

Dim WB1 As Workbook: Set WB1 = Workbooks.Open("C:\test1.csv") ' test1をExcelに読み込む Dim SH1 As Worksheet: Set SH1 = WB1.Sheets(1) ' test1のシート Dim WB2 As Workbook: Set WB2 = Workbooks.Open("C:\test2.csv") ' test2をExcelに読み込む Dim SH2 As Worksheet: Set SH2 = WB2.Sheets(1) ' test2のシート ' ' SH1(test1)を書き換えていく ' Dim rx1 As Long: rx1 = 1 ' test1の行インデクス Do While (SH1.Cells(rx1, "A") <> "") ' test1の終端まで Dim rx2 As Long: rx2 = 1 ' test2の行インデクス Do While (SH2.Cells(rx2, "A") <> "") ' test2の終端まで If (SH2.Cells(rx2, "A") = SH1.Cells(rx1, "A")) Then ' 番号が一致 If (SH2.Cells(rx2, "B") = SH1.Cells(rx1, "B")) Then ' 日付が一致 SH1.Cells(rx1, "C") = Application.WorksheetFunction.Max(SH1.Cells(rx1, "C"), SH2.Cells(rx2, "C").Resize(1, 3)) Exit Do End If End If rx2 = rx2 + 1 Loop rx1 = rx1 + 1 ' インデクスを進める Loop ' ' SH1をコピーして出力ファイルとする。SH1(test1)は保存しないのでもとのまま ' SH1.Copy ' test1をコピーしてworkのbookを作る Dim WBO As Workbook: Set WBO = ActiveWorkbook Call WBO.SaveAs("C:\OutFile1.csv", xlCSV) ' workのBookをCSVで保存 Call WBO.Close(False, , False) Call WB1.Close(False, , False) ' test1:保存しない Call WB2.Close(False, , False) ' test2:保存しない Set SH1 = Nothing Set SH2 = Nothing Set WB1 = Nothing Set WB2 = Nothing Set WBO = Nothing

投稿2019/10/11 02:14

h.horikoshi

総合スコア505

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hj_zebra

2019/10/11 04:05 編集

>h.horikoshiさん 対策方法などを複数提案していただきありがとうございました。 もっと勉強しようと思います。 おかげで解決できました。
guest

0

時刻の比較と、社員番号・日付ごとに取得する処理を考えた時に
naokiさんのコードを変更しようとすると
物凄くめんどくさかったので一から書き直しました。

デバッグで使用したテストファイルと出力したマージ後ファイルの内容

csv

1◆ test1.csv 2123450,2019/01/01,11:25 3123451,2019/01/02,10:25 4123452,2019/01/03,05:25 5123453,2019/01/04,13:25 6◆ test2.csv 7123450,2019/01/01,17:25,23:25,19:25 8123453,2019/01/01,03:25,21:25,21:25 9123451,2019/01/02,19:25,20:25,21:25 10123450,2019/01/02,17:25,23:25,19:25 11123452,2019/01/03,21:25,19:25,15:25 12123450,2019/01/03,12:25,23:25,19:25 13123452,2019/01/04,21:25,10:25,15:25 14123453,2019/01/04,23:25,21:25,21:25 15◆ OutFile.csv 16123450,2019/01/01,23:25 17123451,2019/01/02,21:25 18123452,2019/01/03,21:25 19123453,2019/01/04,23:25 20123453,2019/01/01,21:25 21123450,2019/01/02,23:25 22123450,2019/01/03,23:25 23123452,2019/01/04,21:25

作成したソースコード

lang

1'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 2' 関数:メインルーチン 3'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 4Sub MR_MargeCsvFile() 5 ' CSVファイルを変数に取得 6 Dim path As String 7 Dim file1() As String 8 Dim file2() As String 9 path = ThisWorkbook.path 10 file1 = GetFileData_Txt(path + "\test1.csv") 11 file2 = GetFileData_Txt(path + "\test2.csv") 12 13 ' 社員番号+日時ごとに時刻をマージ 14 Dim dic_marge As Object 15 Set dic_marge = VBA.CreateObject("Scripting.Dictionary") 16 Dim i_yoso, i_yosoc As Integer 17 Dim keyset As String 18 Dim splitval() As String 19 ' ファイル1 20 For i_yoso = 0 To UBound(file1) 21 splitval = split(file1(i_yoso), ",") 22 keyset = splitval(0) + "," + splitval(1) 23 dic_marge.Add keyset, CDate(splitval(2)) 24 Next 25 ' ファイル2 26 For i_yoso = 0 To UBound(file2) 27 splitval = split(file2(i_yoso), ",") 28 keyset = splitval(0) + "," + splitval(1) 29 For i_yosoc = 2 To 4 30 If dic_marge.Exists(keyset) Then 31 If dic_marge.Item(keyset) < CDate(splitval(i_yosoc)) Then 32 dic_marge.Item(keyset) = CDate(splitval(i_yosoc)) 33 End If 34 Else 35 dic_marge.Add keyset, CDate(splitval(i_yosoc)) 36 End If 37 Next 38 Next 39 40 ' マージ後の変数をファイルに出力 41 Dim outvalue As String 42 outvalue = "" 43 For Each keyone In dic_marge 44 If outvalue <> "" Then 45 outvalue = outvalue + vbCrLf 46 End If 47 outvalue = outvalue + keyone + "," 48 outvalue = outvalue + Format(dic_marge.Item(keyone), "hh:nn") 49 Next 50 Call OutFileData("OutFile1.csv", outvalue) 51End Sub 52 53'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 54' 関数:Textファイル読み込み 55'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 56Public Function GetFileData_Txt(str_Pass As String) As String() 57 Dim str_buf As String 58 Dim fso_GetFile As Object 59 Dim fso_GetFileTxt As Object 60 ' ファイル存在判定 61 If Dir(str_Pass) = "" Then 62 ReDim GetFileData_Txt(0) 63 Exit Function 64 End If 65 ' ファイルから変数に格納 66 Set fso_GetFile = CreateObject("Scripting.FileSystemObject") 67 Set fso_GetFileTxt = fso_GetFile.OpenTextFile(str_Pass) 68 str_buf = fso_GetFileTxt.ReadAll 69 Set TextFile = Nothing 70 Set fso = Nothing 71 ' 戻り値設定 72 GetFileData_Txt = split(str_buf, vbCrLf) 73End Function 74 75'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 76' 関数:ファイル出力 77'*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 78Public Sub OutFileData(st_filename As String, st_outdata As String) 79 ' 出力パス設定 ※このブックと同階層に出力する仕様になってるのでそちらで変更してください 80 Dim st_getpass As String 81 st_getpass = ThisWorkbook.path + "\" + st_filename 82 ' ファイルが存在してたら消す 83 If Dir(st_getpass) <> "" Then 84 Kill st_getpass 85 End If 86 ' ファイル出力 87 Open st_getpass For Output As #1 88 Print #1, st_outdata; 89 Close #1 90End Sub

自分勝手に一から書いてすみません!
質問に対応している部分のみ説明しますので
自作したソースに自分で考えて、付け加えてみると勉強になると思います!

Dictionaryオブジェクトの説明
上記サイトを見ていただけると分かると思いますが、このDictionaryオブジェクトは
**「test1.csvの社員番号と日付とtest2.csvの番号と日付一致判定」**に便利です。
今回は、Dictionaryオブジェクトのキーに「社員番号と日付」をいれてます

日付文字列を比較・計算する方法
時刻が文字列のままだと比較ができないので、「CDate("時刻")」関数でDate型にします
一番時刻の遅い値を取得する時に必要な知識だと思うのでご確認ください。

他に説明がいることがあったら教えていただければ極力お答えします。

投稿2019/10/11 02:11

編集2019/10/11 02:23
Youbun

総合スコア125

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hj_zebra

2019/10/11 04:05

>Youbunさん とても分かりやすく回答していただきありがとうございました。 もっと勉強してみようと思います。 おかげで解決できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問