[[20160222175945]] 『サブフォルダを含んでいるCSVの統合』(はる) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『サブフォルダを含んでいるCSVの統合』(はる)

いつもお世話になっております。

ひとつのフォルダを選び、その中の全てのCSVファイル(サブフォルダも含みます)を統合して、新規のエクセル(.xlsx)で保存させたいのです。
ただ、ひとつのキーワードとなる言葉が、CSVの1行の中に入ってたら、その行は結合しないという規則があります。
その行が何行あるのか、何行目にあるのかが、データによって違うのです。
また、キーワードも、処理するひとつのフォルダ(サブフォルダも含みます)によって変わります。(キーワードがないときもあります)

ご教授のほど、回答をよろしくお願い申し上げます。

< 使用 Excel:Excel2007、使用 OS:Windows8 >


この処理にはいくつかの機能が必要というか、いくつかの機能を組み合わせるわけですね。
CSVファイルの中身の参照方法は数多くあるのですが、かりに、一番単純に【ブックとして開く】方法をとるとすれば

1.統合先の新規ブックを作成します
2.指定フォルダ(含むサブフォルダ)の中からCVSファイル名を抽出します。
3.抽出したCSVファイル名ごとに
 1)そのCSVファイルをブックとして開きます
 2)そのブックとして開かれたCSVファイルにシートの1行目から最終行までをループで取り出します。
 3)その行の中に、指定のキーワードがあれば除外、なければ、それを統合先の新規ブックのシートに順々に書きこんでいきます
 4)最後の行までの処理が終われば、このCSVファイルを閉じます
4.次のCSVファイルに対して3.の処理を繰り返して行います
5.新規ブックとして生成した、CSVファイルが統合されたブックを、名前を付けてしかるべきフォルダに保存します。

この中のどの部分の回答を期待していますか?
すべて?

1.と 3.−1)と 3.−4)と 5. については、エクセル上で操作してマクロ記録をとればコードが入手できますから
それ以外のところですかね? それ以外のところでも、すでに はる さんの知識でOKのところもあると思います。

それらを除いた部分、、わからないところを、教えてください。
そうすれば、皆さんからアドバイスがあると思います。

それとも、丸々、お願いということですか?

(β) 2016/02/22(月) 19:56


βさん、ご指摘ありがとうございます。
インターネットで調べて、関係ありそうなコードを調べてみました。
初心者で、申し訳ありません。

フォルダを選択したところから、サブフォルダまで行って、その中のファイルを開くところから、すでにわかりません。。。

::::::::::::::::::::::::::::::::::::::

任意のフォルダにあるファイルをエクセルに書き出す

Sub SampleFoldDir()

    Dim Folder1 As String, FileA As String, LastRow As Long

    'フォルダを任意で選択する
    With Application.FileDialog(msoFileDialogFolderPicker).Show
    Folder1 = .SelectedItems(1)
    End With

    '選んだフォルダの拡張子xlsを含むファイルを返す
    FileA = Dir(Folder1 & "\*.csv")

        'フォルダ内のファイルがなくなるまで繰り返す
        Do While FileA <> ""

        'Dirのpathnameの指定をなくす
        FileA = Dir()

        Loop

End Sub

::::::::::::::::::::::::

新規ブックの作成保存
Sub workbookSaveAs02()

'ローカル変数宣言

    Dim objWorkbook As Workbook

'新規ブックを作成し、変数に格納

    Set objWorkbook = Workbooks.Add

'新規ブックのファイル名を「Book2.xls」にして
'C:\Users\Masaki\Documents以下に保存

    objWorkbook.SaveAs Filename:="C:\Users\Masaki\Documents\Book2.xls", _
                       FileFormat:=xlWorkbookNormal 

End Sub

:::::::::::::::::::::::::::::::::::

あるフォルダの中に存在するすべてのサブフォルダを調べる
Sub Test()
 Dim cnt As Long
 cnt = 0
  Call Sample3("C:\Work")
End Sub

Sub Sample3(Path As String)
  Dim buf As String, f As Object
  buf = Dir(Path & "\*.*")
  Do While buf <> ""
    cnt = cnt + 1
    Cells(cnt, 1) = buf
    buf = Dir()
  Loop
  With CreateObject("Scripting.FileSystemObject")
    For Each f In .GetFolder(Path).SubFolders
      Call Sample3(f.Path)
    Next f
  End With
End Sub

::::::::::::::::::::::::::::::::::::

CSVファイルの読み込み
Sub CSV_Read2()

    Dim FileType, Prompt As String
    Dim FileNamePath As Variant
    Dim textline, csvline() As String
    Dim Rowcnt, ColumNum As Integer
    Dim ch1 As Long

    FileType = "CSV ファイル (*.csv),*.csv"
    Prompt = "CSV File を選択してください"
    '操作したいファイルのパスを取得します
    FileNamePath = SelectFileNamePath(FileType, Prompt)

    If FileNamePath = False Then    'キャンセルボタンが押された
        End
    End If

    '空いているファイル番号を取得します
    ch1 = FreeFile

    'FileNamePath のファイルをオープンします
    Open FileNamePath For Input As #ch1

    'エラーが発生したらファイルを閉じます
    'CSVのファイルは1行の項目数が正確に合っていないと読めないのですが、
    '色々なCSVがあるようなので入れておきます
    On Error GoTo CloseFile

    '表の行番号の初期化 1行目から読み込んだデータを入力します
    Rowcnt = 1

    Do While Not EOF(ch1)      'ファイルの終端かどうかを確認します。

        '1行読み込みます
        Line Input #ch1, textline

        'ダブルクォーテーションを削除します
        'カンマ+ダブルクォーテーションで区切られている CSVファイル
        'などは適時追加してください
        textline = Replace(textline, """", "")

        'カンマで分離します
        csvline() = Split(textline, ",")

        '配列渡しでセルに代入
        Range(Cells(Rowcnt, 1), _
              Cells(Rowcnt, UBound(csvline()) + 1)) = csvline()

        Rowcnt = Rowcnt + 1
    Loop

CloseFile:

    'ファイルを閉じます
    Close #ch1

End Sub

(はる) 2016/02/23(火) 00:46


失礼しました。追加です。

Function SelectFileNamePath(FileType, Prompt) As Variant

    SelectFileNamePath = Application.GetOpenFilename(FileType, , Prompt)
End Function

(はる) 2016/02/23(火) 01:07


上の調べたものを参考にして、つなげてみましたが、限界です。。。
サブフォルダ内のファイルも足りないですし、次のサブフォルダ内の処理になったときに、1行目からの上書きになってしまいます。
仕事にいく時間となり、残念です。このようなコードしか出来ず申し訳ございません。
よろしくお願いいたします。

Sub test2()

 Dim FolderPath As String
    With Application.FileDialog(msoFileDialogFolderPicker)

        If .Show = -1 Then  'アクションボタンがクリックされた
            FolderPath = .SelectedItems(1)
        Else                'キャンセルボタンがクリックされた
            FolderPath = ""
        End If
    End With
'ローカル変数宣言
    Dim objWorkbook As Workbook
'新規ブックを作成し、変数に格納
    Set objWorkbook = Workbooks.Add
    Call Sample3(FolderPath)

End Sub
Sub Sample3(FolderPath As String)

    Dim cnt As Long
        cnt = 0
    Dim ch1, r As Long
    Dim buf As String, f As Object
        buf = Dir(FolderPath & "\*.*")
     Do While buf <> ""
         cnt = cnt + 1
'        Cells(cnt, 1) = buf
          buf = Dir()
      Debug.Print FolderPath & "\" & buf

    Dim FileNamePath As Variant
    Dim textline, csvline() As String
    Dim Rowcnt, ColumNum As Integer

    '操作したいファイルのパスを取得します
   If InStr(buf, ".csv") > 0 Then
      FileNamePath = FolderPath & "\" & buf

    '空いているファイル番号を取得します
       ch1 = FreeFile

    'FileNamePath のファイルをオープンします
       Open FileNamePath For Input As #ch1

    'エラーが発生したらファイルを閉じます
    'CSVのファイルは1行の項目数が正確に合っていないと読めないのですが、
    '色々なCSVがあるようなので入れておきます
    On Error GoTo CloseFile

    '表の行番号の初期化 1行目から読み込んだデータを入力します
      Rowcnt = 1

    Do While Not EOF(ch1)      'ファイルの終端かどうかを確認します。
        '1行読み込みます
        Line Input #ch1, textline

        'ダブルクォーテーションを削除します
        'カンマ+ダブルクォーテーションで区切られている CSVファイル
        'などは適時追加してください
        textline = Replace(textline, """", "")

        'カンマで分離します
        csvline() = Split(textline, ",")

        '配列渡しでセルに代入
        Range(Cells(r + 1, 1), _
              Cells(r + 1, UBound(csvline()) + 1)) = csvline()

        r = r + 1
        Rowcnt = Rowcnt + 1
    Loop

CloseFile:

    'ファイルを閉じます
    Close #ch1
  End If
    Loop
    With CreateObject("Scripting.FileSystemObject")
        For Each f In .GetFolder(FolderPath).Subfolders
            Call Sample3(f.Path)
        Next f
    End With

 End Sub
(はる) 2016/02/23(火) 03:43

 ずいぶん頑張られましたね! 敬服です。
 もう少し、部品だけ、マクロ記録で生成し、ループ処理はギブアップやむなしかな? と思ったんですが。

 作り上げられたコード部品をなるべく生かして、コードを書いてみます。(FSOを使っておられますのでフォルダ選択以外はFSOで書いてみようと思っています)

 午後になるかと思います。それまでに、他の回答者さんから回答があればいいですね。

(β) 2016/02/23(火) 09:59


 以下の3点、教えてください。

 1.キーワードは、完全一致ですか、部分一致ですか?
 2.キーワードが現れる列は固定されていますか(B列とか、C列とか) あるいはどの列にあるかわからないのですか?
 3.CSVファイルの1行目はタイトル行ですか?それとも1行目からデータですか?

(β) 2016/02/23(火) 11:54


 一応書いてみました。

 ・できるだけ はるさんが調べられた FSO を中心に記述しました。FSO は、フォルダからの取り出しが【非常に遅い】です。
  早さを求めるなら DIRコマンドや あるいは API を使う方法もありますが、わかりやすさという点もあり FSOを選びました。
 ・CSVファイルの実際の取り出しは、エクセルブックとして開く方法をとっています。これも【非常に遅い】です。
  速さを求めるなら メモリー内ですべて読みこみ、正規表現などで除外文字列のある行を削除し、それをテキストファイルあたりに書きだしながら
  さいごに新規ブックにそのテキストファイルを取り込むということも考えられますが、これも、わかりやすさ で。

 どうしようもなく遅いということなら、手を入れてもいいですが、もしかしたら上級者さんたちからのコードアップもあるかもしれませんので
 期待して待ちましょう。

 なお、CSVファイルの1行目がタイトル行、除外文字が存在する列は不特定、マッチングは部分一致にしてあります。
 ここも、列が決まっていればフィルター機能を使い、もう少し早い処理に変更できます。

 Sub Sample()
    Dim pPath As String
    Dim pFold As Object
    Dim ignore As String
    Dim fso As Object
    Dim fPool As Variant
    Dim dic As Object
    Dim fPath As Variant
    Dim shT As Worksheet
    Dim shF As Worksheet
    Dim done As Boolean

    Application.ScreenUpdating = False

    Set dic = CreateObject("Scripting.Dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject")

    'フォルダ選択
    With Application.FileDialog(msoFileDialogFolderPicker)
        If Not .Show Then Exit Sub     'キャンセルボタン
        pPath = .SelectedItems(1)
    End With

    '除外文字列指定
    ignore = Application.InputBox("除外文字列を指定してください。もし、除外文字列がない場合は" & vbLf & _
                                "このままOKボタン、あるいはキャンセルボタンを押してください", Type:=2)

    'フォルダからのCSVファイル抽出(含むサブフォルダ)
    Set pFold = fso.GetFolder(pPath)
    GetFiles pFold, dic

    If dic.Count = 0 Then
        MsgBox "ファイルがありません"
        Exit Sub
    End If

    '統合用新規ブックの作成(シートは1枚のみ)
    Set shT = Workbooks.Add(xlWBATWorksheet).Sheets(1)

    '抽出したCSVファイルの取り出し
    For Each fPath In dic

        Set shF = Workbooks.Open(fPath).Sheets(1)
        CutData shF, ignore

        If Not done Then
            shF.Cells.Copy shT.Range("A1")
            done = True
        Else
            shF.Range("A1", shF.UsedRange).Offset(1).Copy shT.Range("A" & Rows.Count).End(xlUp).Offset(1)
        End If

        shF.Parent.Close False

    Next

    '新規ブックを保存。同名のものがあれば無条件上書き。
    Application.DisplayAlerts = False
    shT.Parent.SaveAs Filename:=ThisWorkbook.Path & "\新しいブック名.xlsx", FileFormat:=xlOpenXMLWorkbook
    Application.DisplayAlerts = True

    Application.Goto shT.Range("A1")
    Application.ScreenUpdating = True

    MsgBox "統合完了"

 End Sub

 'FSO 再帰処理でフォルダ内のCSVファイルを抽出
 Private Sub GetFiles(pFold As Object, dic As Object)
    Dim myFile As Object
    Dim myFolder As Object

    'ファイル名を列挙
    For Each myFile In pFold.Files
        If LCase(Right(myFile.Path, 4)) = ".csv" Then dic(myFile.Path) = True
    Next myFile

    'サブフォルダを検索
    For Each myFolder In pFold.subfolders
        Call GetFiles(myFolder, dic)
    Next myFolder

 End Sub

 '除外文字列の処理
 Private Sub CutData(sh As Worksheet, ignore As String)
    Dim c As Range
    Dim f As Range
    Dim r As Range

    If ignore = "" Then Exit Sub

    Set c = sh.UsedRange.Find(What:=ignore, LookAt:=xlPart)
    If c Is Nothing Then Exit Sub
    Set f = c

    Do
        If r Is Nothing Then
            Set r = c.EntireRow
        Else
            Set r = Union(r, c.EntireRow)
        End If

        Set c = sh.UsedRange.FindNext(c)

    Loop While c.Address <> f.Address

    r.Delete

 End Sub

(β) 2016/02/23(火) 17:56


    ignore = Application.InputBox("除外文字列を指定してください。もし、除外文字列がない場合は" & vbLf & _
                                "このままOKボタン、あるいはキャンセルボタンを押してください", Type:=2)

 これを

    ignore = Application.InputBox("除外文字列を指定してください。" & vbLf & _
                                "もし除外文字列がない場合はこのままOKボタンを押してください", Type:=2)
    If ignore = "False" Then Exit Sub   'キャンセルボタン

 に変更してください。

(β) 2016/02/23(火) 20:00


β様、本当にありがとうございます!
お恥ずかしい支離滅裂なコードを盛り込んで下さって、ありがとうございます。
目で追うだけでも嬉しいです。せっかくのご質問への返事が遅れてしまい、申し訳ございません。

1.キーワードは、完全一致ですか、部分一致ですか?

            完全一致です。(キーワードがない時もあります)
 2.キーワードが現れる列は固定されていますか(B列とか、C列とか) あるいはどの列にあるかわからないのですか?
             A列になります。
3.CSVファイルの1行目はタイトル行ですか?それとも1行目からデータですか?
             1行目は、どちらの場合もあります。

FSOは、非常に遅い。。。だったんですね。そんなことも知り得ませんでした。
DIRというのがコードの中に入っているものでも何度も試してみたのですが、
ステップインで確かめるたびにファイル名などが行ったり来たりするようなものしか作れず、頭が混乱してしまいました。
早速今夜、β様のコードをステップインして勉強します。
深夜になると思いますが、理解できましたら、DIRのコードの勉強もしようと考えております。
しつこいようで申し訳ございません。よろしくお願いいたします。
(つづく)

(はる) 2016/02/23(火) 20:16


 ところで、明日、早朝から、遠出予定で、戻りは金曜日になります。
 それまでは、なかなかネットを開くことができないと思いますのでよろしくお願いします。

 コメントしましたように、アップしたコードは、部分一致、かつ全列をチェックしています。
 また、1行目がタイトル行という前提で書きましたので、最初のファイルは先頭から、2番目以降のファイルは
 1行目をカットして2行目から取り込んでいます。

 そのあたりは、最後に直しましょう。

(β) 2016/02/23(火) 20:51


βさま、承知しました。ゆっくりとステップインで勉強を続けます。

(はる) 2016/02/23(火) 21:22


頑張ってコーディングされたようなので、多少難しいロジックでも大丈夫と判断し、別案を書きます。
シートにActiveXのボタンを2個貼って、以下をシートモジュールのマクロとしてください。
(ボタンを使わずにそのままでも使えます)

対象となる親フォルダは、毎回変わるものではないと思うので、Const定義にしています。書き換えてください。
使い方は、まずCommandButton1を押下すると、指定フォルダ以下のフォルダ一覧を作成します。
フォルダ毎に異なるという除外対象のキーワードを、ここでB列に手入力してください。

キーワード入力が終わった後、CommandButton2を押下することで、新シートを作成します。

 Const cPATH = "C:\test\"

 Private Sub CommandButton1_Click()
    Dim cFiles As Variant
    Dim i As Long

    Columns("A:B").ClearContents
    Range("A1").Resize(1, 2) = Array("フォルダ", "キーワード")

    cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /AD/B/S """ & cPATH & "*.*""").StdOut().ReadAll(), vbNewLine)
    For i = 0 To UBound(cFiles) - 1
        Cells(i + 2, "A").Value = cFiles(i)
    Next i
 End Sub

 Private Sub CommandButton2_Click()
    Dim F1 As Integer
    Dim vw As Variant
    Dim cFiles As Variant
    Dim cKey As String
    Dim cw As String
    Dim i As Long
    Dim j As Long
    Dim jMax As Long
    Dim iR As Long
    Dim iSt As Long
    Dim bw() As Byte
    Dim csvline() As String

    jMax = Cells(Rows.Count, "A").End(xlUp).Row
    If jMax < 2 Then Exit Sub

    Application.ScreenUpdating = False

    With Workbooks.Add(xlWBATWorksheet).Sheets(1)
        cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /A-D/B/S """ & cPATH & "*.csv""").StdOut().ReadAll(), vbNewLine)
        For i = 0 To UBound(cFiles) - 1
            cKey = ""
            For j = 2 To jMax
                If cFiles(i) Like Cells(j, "A").Value & "*" Then
                    cKey = "*" & Cells(j, "B").Value & "*"
                    Exit For
                End If
            Next j

            ReDim bw(FileLen(cFiles(i)) - 1)
            F1 = FreeFile
            Open cFiles(i) For Binary As #F1
            Get #F1, , bw
            Close #F1

            vw = Split(StrConv(bw, vbUnicode), vbCrLf)
            If 0 < iR Then
                iSt = 1
            End If

            For j = iSt To UBound(vw) - 1
                If cKey = "" Or Not vw(j) Like cKey Then
                    csvline() = Split(Replace(vw(j), """", ""), ",")
                    If 0 <= UBound(csvline()) Then
                        iR = iR + 1
                        .Cells(iR, "A").Resize(1, UBound(csvline()) + 1) = csvline()
                    End If
                End If
            Next j
        Next i
    End With

    Application.ScreenUpdating = True
 End Sub

ロジックの大きな特徴は、以下。
・サブフォルダ以下を探す部分を、コマンドプロンプトのDIRコマンドに任せることで単純化。
・ファイル読込を1回で行うことで、高速化。
(???) 2016/02/24(水) 11:12


(???)さま、ありがとうございます。

勉強になります。早速、ステップインしてみます。
その前に。。。 とても初歩的な質問で恐縮です。 
コード(英字の)のことを、もしかしたら 本当は <ロジック> と呼ぶのでしょうか?

(はる) 2016/02/24(水) 19:47


(???)さま。

        cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /A-D/B/S """ & cPATH & "*.csv""").StdOut().ReadAll(), vbNewLine)

の部分、インターネットで調べたところ、cmdというもので、この黒い画面でサブフォルダ以下のファイルが一度に判明するのですね。

勉強のため、昨日β様に教わったフォルダ選択ダイアログにして実行しました。
新規ブックにデータがなにも表示されなかったので、何回もステップインで確かめてみました。
さらに勉強のため調べながら、いくつか付け加えてみましたが、正しい書き方を教えていただけますでしょうか? 

1.新規ブックを2シートにして、 ”ファイル一覧”を追加しました。
  (???)さまの、Array を使わせてもらって、行数などが表示されるように所々にコードを加えました。

2.cKey = "*" & Cells(j, "B").Value & "*"
  の部分で、キーが何も設定されていなかったので、ThisWorkbook.ActiveSheet. をつけたら設定されました。

3.部分一致を完全一致にしました。
  cKey = "*" & Cells(j, "B").Value & "*" → cKey = ThisWorkbook.ActiveSheet.Cells(j, "B").Value

4.少ないサンプルで試してるので正確ではないかもしれないのですが、統合データが1行づつ足りなかったので、

            If 0 < iR Then
                iSt = 1
            End If
  をコメントアウトしたら行数は合いました。この部分はどのようにしたらいいでしょうか? 

5.キーワードは、必ずA列なので、
   If cKey = "" Or Not vw(j) Like cKey Then → If cKey = "" Or Split(vw(j), ",")(0) <> cKey Then
  としました。

よろしくお願いいたします。

:::::::::::::::::::::::::::::::::::::::::::::::

Option Explicit
Public cPATH As String

  Private Sub CommandButton1_Click()
  With Application.FileDialog(msoFileDialogFolderPicker)
        If Not .Show Then Exit Sub     'キャンセルボタン
        cPATH = .SelectedItems(1) & "\"
    End With

    Dim cFiles As Variant
    Dim i As Long

    Columns("A:B").ClearContents
    Range("A1").Resize(1, 2) = Array("フォルダ", "キーワード")

    cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /AD/B/S """ & cPATH & "*.*""").StdOut().ReadAll(), vbNewLine)
    For i = 0 To UBound(cFiles) - 1
        Cells(i + 2, "A").Value = cFiles(i)

    Next i
 End Sub

 Private Sub CommandButton2_Click()
    Dim W As Object
    Dim F1 As Integer
    Dim vw As Variant
    Dim cFiles As Variant
    Dim cKey As String
    Dim cw As String
    Dim tr As String
    Dim i As Long
    Dim j As Long
    Dim cnt As Long
    Dim kwd As Long
    Dim jMax As Long
    Dim iR As Long
    Dim iSt As Long
    Dim bw() As Byte
    Dim csvline() As String

    jMax = Cells(Rows.Count, "A").End(xlUp).Row
    If jMax < 2 Then Exit Sub

    Application.ScreenUpdating = False

    Application.SheetsInNewWorkbook = 2
    Set W = Workbooks.Add
        W.Worksheets(1).Name = "ファイル一覧"
        W.Worksheets(2).Name = "統合データ"
        W.Worksheets("ファイル一覧").Range("A1").Resize(1, 6) = Array("", "フォルダ", "ファイル名", "キーワード数", "行数", "全体行数")

    With W.Worksheets("統合データ")
        cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /A-D/B/S """ & cPATH & "*.csv""").StdOut().ReadAll(), vbNewLine)
        For i = 0 To UBound(cFiles) - 1
            cKey = ""
            For j = 2 To jMax
                 If cFiles(i) Like ThisWorkbook.ActiveSheet.Cells(j, "A").Value & "*" Then
                    cKey = ThisWorkbook.ActiveSheet.Cells(j, "B").Value
                    Exit For
                End If
            Next j

            ReDim bw(FileLen(cFiles(i)) - 1)
            F1 = FreeFile
            Open cFiles(i) For Binary As #F1
            Get #F1, , bw
            Close #F1

            vw = Split(StrConv(bw, vbUnicode), vbCrLf)
'            If 0 = iR Then
'                iSt = 1
'            End If

             cnt = 0
             kwd = 0
            For j = iSt To UBound(vw) - 1
'                If cKey = "" Or Not vw(j) Like cKey Then
'                If InStr(vw(j), cKey) = 0 Then
                If cKey = "" Or Split(vw(j), ",")(0) <> cKey Then
                    csvline() = Split(Replace(vw(j), """", ""), ",")
                    If 0 <= UBound(csvline()) Then
                        iR = iR + 1
                        .Cells(iR, "A").Resize(1, UBound(csvline()) + 1) = csvline()
                        cnt = cnt + 1
                    End If
                  Else
                     kwd = kwd + 1
                End If
            Next j

            With W.Worksheets("ファイル一覧").Cells(i + 2, 1)
                .Offset(, 0).Value = i + 1
              If tr <> Left(cFiles(i), InStrRev(cFiles(i), "\") - 1) Then
                .Offset(, 1).Value = Left(cFiles(i), InStrRev(cFiles(i), "\") - 1)
                  tr = .Offset(, 1).Value
              End If
                .Offset(, 2).Value = Dir(cFiles(i))
                .Offset(, 3).Value = kwd
                .Offset(, 4).Value = cnt
                .Offset(, 5).Value = kwd + cnt
             End With
        Next i
    End With

   W.SaveAs Filename:=cPATH & "CSV統合データ.xlsx", _
                       FileFormat:=xlOpenXMLWorkbook

    Application.ScreenUpdating = True
 End Sub

(はる) 2016/02/25(木) 00:22


β様、お世話になります。
インターネットで調べながらステップインしていると、時間があっという間に過ぎていきます。

 'FSO 再帰処理でフォルダ内のCSVファイルを抽出
の部分が、大変難しいです。。。 ENDまで行っているのに、また上に昇る動き。。。もう少しステップインで理解を深めて行きたいです。

β様のご質問、1〜3 の部分を失礼を承知の上、インターネットで調べながら直してみましたが、モタモタした感じです。。。申し訳ありません。
正解のコードを教えていただけますでしょうか。

それと、新規ブックにファイル一覧というシートを挿入して、行数などを記録したら確認が出来ていいかなと思い追加してみました。
こちらも調べながらの作業でしたので、正解を どうかよろしくお願いいたします。

ご面倒な続きをお願いして、申し訳ございません。

Option Explicit

 Public cnt As Long
 Public kwd As Long
 Public auti As Long
 Public maxi As Long

 Sub Sample()
    Dim pPath As String
    Dim pFold As Object
    Dim ignore As String
    Dim fso As Object
    Dim fPool As Variant
    Dim dic As Object
    Dim fPath As Variant
    Dim shT As Worksheet
    Dim shF As Worksheet
    Dim shM As Worksheet
    Dim done As Boolean
    Dim W As Object
    Dim tr As String
    Dim i As Long

    Application.ScreenUpdating = False

    Set dic = CreateObject("Scripting.Dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject")

    'フォルダ選択
    With Application.FileDialog(msoFileDialogFolderPicker)
        If Not .Show Then Exit Sub     'キャンセルボタン
        pPath = .SelectedItems(1)
    End With

    '除外文字列指定
    ignore = Application.InputBox("除外文字列を指定してください。" & vbLf & _
                                "もし除外文字列がない場合はこのままOKボタンを押してください", Type:=2)
    If ignore = "False" Then Exit Sub   'キャンセルボタン

    'フォルダからのCSVファイル抽出(含むサブフォルダ)
    Set pFold = fso.GetFolder(pPath)
    GetFiles pFold, dic

    If dic.Count = 0 Then
        MsgBox "ファイルがありません"
        Exit Sub
    End If

    '統合用新規ブックの作成(シートは2枚のみ)
    Set shT = Workbooks.Add(xlWBATWorksheet).Sheets(1)
        shT.Name = "統合データ"
        Worksheets.Add(Before:=Worksheets(Worksheets.Count)).Name = ("ファイル一覧")
    Set shM = Worksheets("ファイル一覧")
        shM.Range("A1").Resize(1, 6) = Array("", "フォルダ", "ファイル名", "キーワード数", "行数", "全体行数")

             i = 2

    '抽出したCSVファイルの取り出し
    For Each fPath In dic

        Set shF = Workbooks.Open(fPath).Sheets(1)
        CutData shF, ignore

        If Not done Then
            shF.Cells.Copy shT.Range("A1")
            done = True
        Else
            shF.Range("A1", shF.UsedRange).Copy shT.Range("A" & Rows.Count).End(xlUp).Offset(1)
        End If
            With shM.Cells(i, 1)
                .Offset(, 0).Value = i - 1
              If tr <> Left(fPath, InStrRev(fPath, "\") - 1) Then
                .Offset(, 1).Value = Left(fPath, InStrRev(fPath, "\") - 1)
                  tr = .Offset(, 1).Value
              End If
                .Offset(, 2).Value = Dir(fPath)
                .Offset(, 3).Value = kwd
                .Offset(, 4).Value = maxi - kwd
                .Offset(, 5).Value = maxi
                  i = i + 1
             End With

        shF.Parent.Close False
    Next

    '新規ブックを保存。同名のものがあれば無条件上書き。
    Application.DisplayAlerts = False
    shT.Parent.SaveAs Filename:=ThisWorkbook.Path & "\CSV統合データ.xlsx", FileFormat:=xlOpenXMLWorkbook
    Application.DisplayAlerts = True

    Application.Goto shT.Range("A1")
    Application.ScreenUpdating = True

    MsgBox "統合完了"

 End Sub

 'FSO 再帰処理でフォルダ内のCSVファイルを抽出
 Private Sub GetFiles(pFold As Object, dic As Object)
    Dim myFile As Object
    Dim myFolder As Object

    'ファイル名を列挙
    For Each myFile In pFold.Files
        If LCase(Right(myFile.Path, 4)) = ".csv" Then dic(myFile.Path) = True
    Next myFile

    'サブフォルダを検索
    For Each myFolder In pFold.Subfolders
        Call GetFiles(myFolder, dic)
    Next myFolder

 End Sub

 '除外文字列の処理
 Private Sub CutData(sh As Worksheet, ignore As String)
'    Dim c As Range
'    Dim f As Range
    Dim r As Range
    Dim auti As Long

    If ignore = "" Then Exit Sub

    maxi = Cells(Rows.Count, 1).End(xlUp).Row

' Set c = sh.UsedRange.Find(What:=ignore, LookAt:=xlPart)

    Rows(1).Insert
    Range("A1:B1").AutoFilter Field:=1, Criteria1:=ignore
    auti = Cells(Rows.Count, 1).End(xlUp).Row

    Set r = Range("A2:A" & auti).SpecialCells(xlCellTypeVisible)
        kwd = r.Count
        r.EntireRow.Delete
        sh.AutoFilterMode = False
        Rows(1).Delete

' If c Is Nothing Then Exit Sub
' Set f = c
'
'
'
' Do
' If r Is Nothing Then
' Set r = c.EntireRow
' Else
' Set r = Union(r, c.EntireRow)
' End If
'
'
'
' Set c = sh.UsedRange.FindNext(c)
'
'
'
' Loop While c.Address <> f.Address
'
'
'
' r.Delete

 End Sub

(はる) 2016/02/25(木) 03:48


まず用語から。
プログラムを作成する事をコーディングと言いますので、「コード」が英語で書くプログラム用文字列を指す事で間違いありません。
ただ、普通にコードと言うと、コーディングした全体を指します。

ロジックとは、論理構造とか仕組みとか考え方の意味で使っています。プログラムの一部とか、サブプロシジャとかサブルーチンとかの狭い範囲になります。

ちなみに、黒いウィンドウはコマンドプロンプトです。昔のMS-DOSのコマンドが使えるやつですね。
VBAのDir関数では、指定した階層のフォルダしか拾えない、しかも1つだけ、という不便さがありますが、
DOSコマンドの方はサブフォルダまでまとめて表示できるオプションがあるのです。
これを利用し、更にコマンドの終了待ちや行の分解を精錬していったのが、ご指摘の1行です。
まぁ、ここはDIRコマンド後のオプションだけ理解して、あとはブラックボックス状態でも構いませんよ。
(正攻法は再帰(自分で自分を呼び出すことで、サブのサブを処理)なのですが、これはコーディングにコツがあり、理解が難しいと思っています)

次にコーディング部分。
フォルダ選択ダイアログに変えてみる、というトライは、とても良いですね。コーディングもきっちり目的に合わせて改造してますし。
その調子でいくと、すぐに質問する必要がなくなるくらいに上達しますよ。

改良するとして、選択したフォルダのフルパス文字列を、どこかのセルに保存しておいてはいかがでしょう?
そうすれば、2つ目のボタンを押したときは、ダイアログから選択する必要がなくなりますよね。

ThisWorkbook.ActiveSheet. を付けないとキーワードが拾えなかった、というのは、ブックを分けたためですね。
なお、ThisWorkbook.ActiveSheet. の部分は、Me. に置き換えられそうです。お試しください。

iSt = 1 の部分について。
ここは、1つ目のシートだけ1行目(タイトル行)もコピーし、2シート目からは2行目以降になるようにしました。
タイトル行は無いという事でしょうか? そうであれば、常に iSt = 0 でOKです。
(???) 2016/02/25(木) 10:35


To はるさん

まだエクセルの環境がないので、とりあえずざくっとしたコメントだけ。
上級者版は、???さんにお任せするとして、βがアップした基礎編で。

・部分一致
 完全一致なら 一か所ある Dindメソッドの xlPart を xlWhole に。
・対象列はA列のみ
 FindメソッドとFindNextメソッドの UsedRange を UsedRange.Columns("A") に。
 (本来はフィルタリングが使えるところですが、後述のタイトル行の話があるので、とりあえずは FindとFindNextで)
・タイトル行があるもののあるし、ないものもある
 う〜ん、これはやっかいですね。 
 タイトル行にこだわったのは、最初のファイルはタイトル行付き、2番目のファイルからは、タイトル行をのぞいたデータ行のみを
 貼り付けたいだろうとかんがえたからです。
 もし、タイトル行のあるものは、2つめのファイル以降もタイトル行含めてコピペなら、簡単ですが、タイトル行がある場合は2行目から、
 タイトル行がない場合は1行目からということになると、最初の行がタイトル行かどうかの判断が必要です。
 判断できる基準はありますか?

(β) 2016/02/25(木) 17:48


(???)さま。
<コード> と <ロジック> の意味が大変よくわかりました。
インターネットで検索しても、問題外の質問の答えが載っているはずもなく、VBAの意味以前の疑問も解決しないままの状態でした。ありがとうございます。
論理。。。ロジックという言葉を使えるようになるのは、私の場合、論理的にも何十年も先のようです。

iSt = 1 の部分についてですが、納得です、なるほどです。そうだったのですね。
最初の行は、タイトル行ではないのです。誠に失礼致しました。

コマンドプロンプト、DOSコマンドなど内容はわからなくても、このような便利な存在を利用する意味を教えていただけだけでも大変勉強になりました。

・フォルダのフルパス文字列を、どこかのセルに保存
・ThisWorkbook.ActiveSheet. の部分は、Me. に置き換えられそうです。

早速勉強してみます。

 

βさま。
お忙しいところ、コメントをありがとうございます。

・xlPart を xlWhole に。
・UsedRange を UsedRange.Columns("A") に。
 
ご指摘の箇所を、確かめてみます。

タイトル行ですが、私の説明不足でご迷惑をおかけしております。。。
ひとつのCSVブックのデータ途中に、何行も出現するタイトル行を統合データに加えないために、キーワードの設定をしたいのです と言ったほうが正解だったかもしれません。。。。(純粋にデータだけの統合です)
せっかくコードを反映させてくださったのに、今頃になって大変申し訳ありません!

(はる) 2016/02/25(木) 22:06


 >>ひとつのCSVブックのデータ途中に、何行も出現するタイトル行を統合データに加えないために、キーワードの設定をしたいのです 

 なるほど。タイトル行を(すべて)加えないようにするなら

 shF.Range("A1", shF.UsedRange).Offset(1).Copy shT.Range("A" & Rows.Count).End(xlUp).Offset(1)

 これを

 shF.Cells.Copy shT.Range("A" & Rows.Count).End(xlUp).Offset(1)

 にしてください。

 ただ、これだと、最初のタイトル行もカットされますが。
 統合されるファイルの先頭にだけはタイトル行をいれたいということなら、もうひとひねり必要ですが。

 なお、おもしろそうなので、???さんと同じく DIRコマンドでの抜出し、標準機能のファイル読みこみに加え
 正規表現でのタイトル除外、標準機能でのファイル統合、そのエクセルへの抽出 といった流れのコードも
 (自分自身の勉強のために)書いてみようかなと思っています。

(β) 2016/02/26(金) 06:53


β様。ありがとうございます。

・ただ、これだと、最初のタイトル行もカットされますが。

そうですね。。。
では、キーワードの行を1行だけ統合データの一番上にコピーする ようにできますでしょうか?
途中で変更して、申し訳ありません。
正規表現。。。想像することもできませんが、どうかよろしくお願いいたします。
 
 
 
(???)さま。
・フォルダのフルパス文字列を、どこかのセルに保存
 したところ、2番目のボタンを続けて何回押下しても、フォルダが変わることが無くなり、復習しやすくなりました。
・Me という便利なキーワードは、シートモジュールにだけ使えるものなのですね。

大変勉強になりました。
 
 

(はる) 2016/02/26(金) 15:21


 正規表現版は、これから書いてみようかなと。でも、根気が続かず、途中で放り投げることもありますから
 期待しないでください。

 とりあえず基礎編で、最初のみタイトル行をセットするコードです。フルセット、以下。

 Sub Sample2()
    Dim pPath As String
    Dim pFold As Object
    Dim ignore As String
    Dim fso As Object
    Dim fPool As Variant
    Dim dic As Object
    Dim fPath As Variant
    Dim shT As Worksheet
    Dim shF As Worksheet
    Dim done As Boolean

    Application.ScreenUpdating = False

    Set dic = CreateObject("Scripting.Dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject")

    'フォルダ選択
    With Application.FileDialog(msoFileDialogFolderPicker)
        If Not .Show Then Exit Sub     'キャンセルボタン
        pPath = .SelectedItems(1)
    End With

    '除外文字列指定
    ignore = Application.InputBox("除外文字列を指定してください。もし、除外文字列がない場合は" & vbLf & _
                                "このままOKボタン、あるいはキャンセルボタンを押してください", Type:=2)

    'フォルダからのCSVファイル抽出(含むサブフォルダ)
    Set pFold = fso.GetFolder(pPath)
    GetFiles pFold, dic

    If dic.Count = 0 Then
        MsgBox "ファイルがありません"
        Exit Sub
    End If

    '統合用新規ブックの作成(シートは1枚のみ)
    Set shT = Workbooks.Add(xlWBATWorksheet).Sheets(1)

    '抽出したCSVファイルの取り出し
    For Each fPath In dic

        Set shF = Workbooks.Open(fPath).Sheets(1)
        CutData shF, ignore, done

        If Not done Then
            shF.Cells.Copy shT.Range("A1")
            done = True
        Else
            shF.Range("A1", shF.UsedRange).Copy shT.Range("A" & Rows.Count).End(xlUp).Offset(1)
        End If

        shF.Parent.Close False

    Next

    '新規ブックを保存。同名のものがあれば無条件上書き。
    Application.DisplayAlerts = False
    shT.Parent.SaveAs Filename:=ThisWorkbook.Path & "\新しいブック名.xlsx", FileFormat:=xlOpenXMLWorkbook
    Application.DisplayAlerts = True

    Application.Goto shT.Range("A1")
    Application.ScreenUpdating = True

    MsgBox "統合完了"

 End Sub

 'FSO 再帰処理でフォルダ内のCSVファイルを抽出
 Private Sub GetFiles(pFold As Object, dic As Object)
    Dim myFile As Object
    Dim myFolder As Object

    'ファイル名を列挙
    For Each myFile In pFold.Files
        If LCase(Right(myFile.Path, 4)) = ".csv" Then dic(myFile.Path) = True
    Next myFile

    'サブフォルダを検索
    For Each myFolder In pFold.subfolders
        Call GetFiles(myFolder, dic)
    Next myFolder

 End Sub

 '除外文字列の処理
 Private Sub CutData(sh As Worksheet, ignore As String, done As Boolean)
    Dim c As Range
    Dim f As Range
    Dim r As Range
    Dim myR As Range

    If ignore = "" Then Exit Sub

    With sh.UsedRange.Columns(1)
        If done Then
            Set myR = .Cells
        Else
            Set myR = .Offset(1).Resize(.Rows.Count - 1)
        End If
    End With

    Set c = myR.Find(What:=ignore, LookAt:=xlPart)
    If c Is Nothing Then Exit Sub
    Set f = c

    Do
        If r Is Nothing Then
            Set r = c.EntireRow
        Else
            Set r = Union(r, c.EntireRow)
        End If

        Set c = myR.FindNext(c)

    Loop While c.Address <> f.Address

    r.Delete

 End Sub

(β) 2016/02/26(金) 20:37


β様、ありがとうございます。

実行してみたところ、統合シート1行目に、なぜかタイトル行が表示されていません。。。
もう少し やってみます。

(はる) 2016/02/27(土) 04:50


 >>統合シート1行目に、なぜかタイトル行が表示されていません。。。 

 各ファイルの1行目が必ずタイトル行という認識で、最初のファイルは2行目以降の除外、2つめのファイルからは1行目からの除外にしています。

 なので、もし、必ずしも1行目がタイトル行ではない。だけど、どこかに必ずタイトル行があるということなら
 もう1ひねりします。

 もし、最初のファイル(どれが最初かは、その時のフォルダ内のファイルのファイル名によりますが)が
 タイトル行を全く持っていないということなら、ちょっとお手上げですけど。

(β) 2016/02/27(土) 06:11


 もし、指摘の状況が、1行目が必ずしもタイトル行ではない、でも、どこかにタイトル行があれば
 それを統合ファイルの1行目にタイトル行としてセットしたいという要件であれば、CutData のみ以下に。
 そうではない、1行目にタイトル行があったのになくなった!! ということなら悩んでしまいますが・・

 '除外文字列の処理
 Private Sub CutData(sh As Worksheet, ignore As String, done As Boolean)
    Dim c As Range
    Dim f As Range
    Dim r As Range
    Dim myR As Range
    Dim sv As Variant

    If ignore = "" Then Exit Sub

    Set myR = sh.UsedRange.Columns(1)

    Set c = myR.Find(What:=ignore, LookAt:=xlPart)
    If c Is Nothing Then Exit Sub
    Set f = c
    If Not done Then sv = sh.UsedRange.Rows(c.Row)
    Do
        If r Is Nothing Then
            Set r = c.EntireRow
        Else
            Set r = Union(r, c.EntireRow)
        End If

        Set c = myR.FindNext(c)

    Loop While c.Address <> f.Address

    r.Delete
    If IsArray(sv) Then
        sh.Rows(1).Insert
        sh.Range("A1").Resize(, UBound(sv, 2)).Value = sv
    End If
 End Sub

(β) 2016/02/27(土) 09:38


β様、何度も申し訳ございません。
このコメントを書いてる間に、β様が回答を更新して下さっていました。
ありがとうございます!

1行目が必ずタイトル行ではなく、
タイトル行が全くないファイルはありません。

Sub Sample2 の、最初のコードの内容ですが、

If Not done Then

            shF.Cells.Copy shT.Range("A1")
            done = True

この部分で、最初のファイルを統合シートにそのままコピー
そのあと、Private Sub CutData で、1列目の
始まりの行を含めた範囲を設定、
(更新コードでは、1列目全てがセル範囲。最高のファイルの時のキーワードの行をsvに設定。)
1列目にキーワードがある行を見つけて、Union(r, c.EntireRow) で、キーワードの行を r に追加していき、
最後に r をいっぺんに削除する。
(更新コードでは、この後タイトル行を1行目に挿入する。)。。のような感じでしょうか?
勘違いしている所を指摘して下さい。

doneを使うこと、EntireRow、Union(r, c.EntireRow)、変数(?)の設定の仕方など、
初めて調べることばかりで、全てのコードが大変勉強になります。
今から、難しいサブフォルダ部分をステップインします。

1点だけ質問させてください。
更新して下さったコードの最後で、タイトル行を挿入する所です。
列の最大数を調べる UBound(sv, 2) という部分です。
この、 2 を調べると、2次元ということだそうですが、2次元とは列のことなのでしょうか?
長くなりましたが、よろしくお願いいたします。

(はる) 2016/02/27(土) 12:11


 >>Sub Sample2 の、最初のコードの内容ですが、〜 勘違いしている所を指摘して下さい。

 その理解で結構かと思います。

 >> 今から、難しいサブフォルダ部分をステップインします。 

 いわゆる再帰処理、自分の中から自分自身を呼び出す処理でわかりにくいかもしれませんが、がんばってください。

 >>2次元とは列のことなのでしょうか?

 配列には列だけを持つ1次元配列、行と列を持つ2次元配列(シートのセル領域のようなもの)、さらには3次元、4次元・・とあります。
 で、sv には 最初に現れたタイトル行1行を取り込んでいます。このとき、sv は 1行、複数列の2次元配列になります。
 2次元配列で UBound(配列,1) ・・つまり配列の1次元目の項目数(行数)・・今回のケースでは常に1、
 UBound(配列,2) ・・つまり配列の2次元目の項目数(列数)・・が実際のCSVファイルの列数になります。

 なお、ちょっと申し上げていた、別処理コードを書いてみました。
 参考うまでに、レスを変えてアップします。

(β) 2016/02/27(土) 14:18


 ちなみに、こちらで持っている、フォルダ(サブフォルダも含める)からのファイル名抽出処理ですが
 処理の早いものから

 1.APIのFindFirstFileやFindNextFileによる処理
 2.DIRコマンド(RUN)を使った処理
 3.ShellApplicationを使った処理
 4.DIRコマンド(Exec)を使った処理(???さんから提示の方法です)
 5.ぐ〜んと効率が悪くなって FSO

 となっています。

 ただし、たとえばサブフォルダも含めて、500個ぐらいのファイルであれば、1.〜4.は (こちらの環境で)0.5秒〜2秒、
 5.でも 10秒弱ですから、そこはわかりやすいものを使えばいいと思います。
 むしろ、そのあと、抽出されたファイル名に従って、そのファイルを取り込んで統合する、そこの処理が
 処理時間全体にとってはインパクトがある部分で、基礎編としてアップした、【ブックとして開く】、これが
 きわめて遅い処理になります。ここは、???さんが提示されたようなVBA標準のファイル処理を使う、あるいは
 特定のファイルを開くわけですから、FSOのテキストファイル処理機能を使っても、ブックとして開くことに比較すれば
 かなり効率はよくなるはず。

 以下のコードでは、

 ・ShellApplication でファイル一覧を取得
 ・各ファイルはFSOのテキストファイル処理機能を使って開き
 ・除外文字列を正規表現で一括処理をして
 ・それを、やはりFSOを使って統合テキストファイルに書きこみ
 ・最後に、そのテキストファイルを、ブックとして開いてエクセルブックとして保存

 こういったことをしてみました。

 大急ぎで書きなぐった感があり、おおよその確認はしましたが、どこかに間違いがあるかもしれません。
 正規表現処理も、もっとスマートなパターンの組み立てもできるかもしれませんが、力技です。

(β) 2016/02/27(土) 14:22


 Sub Sample4()
    Dim pPath As Variant
    Dim pFold As Object
    Dim ignore As String
    Dim fso As Object
    Dim fPool As Variant
    Dim dic As Object
    Dim fPath As Variant
    Dim shT As Worksheet
    Dim shF As Worksheet
    Dim done As Boolean
    Dim reg As Object
    Dim wsh As Object
    Dim tmpPath As String
    Dim rtn As Long
    Dim fNo As Integer
    Dim buf() As Byte
    Dim s As String
    Dim outPath As String
    Dim outTxt As Object
    Dim inTxt As Object
    Dim objFolder As Object
    Dim mt As Object
    Dim sv As String

    Application.ScreenUpdating = False

    Set dic = CreateObject("Scripting.Dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set reg = CreateObject("VBScript.RegExp")
    Set wsh = CreateObject("WScript.SHell")

    reg.Global = True

    'フォルダ選択
    With Application.FileDialog(msoFileDialogFolderPicker)
        If Not .Show Then Exit Sub     'キャンセルボタン
        pPath = .SelectedItems(1)
    End With

    '除外文字列指定
    ignore = Application.InputBox("除外文字列を指定してください。もし、除外文字列がない場合は" & vbLf & _
                                "このままOKボタン、あるいはキャンセルボタンを押してください", Type:=2)

    'フォルダからのCSVファイル抽出(含むサブフォルダ)
    Set objFolder = CreateObject("Shell.Application").Namespace(pPath)
    Call GetFiles(objFolder.Items, dic)

    If dic.Count = 0 Then
        MsgBox "ファイルがありません"
        Exit Sub
    End If

    '作業用出力ファイルの作成
    outPath = Environ$("Temp") & "\out.tmp"
    On Error Resume Next
    Kill outPath
    On Error GoTo 0

    Set outTxt = fso.OpenTextFile(Filename:=outPath, IOMode:=8, Create:=True)   '8:ForAppending

    '抽出されたcsvファイルの読み込みと出力ファイルへの書き込み
    For Each fPath In dic
        Set inTxt = fso.OpenTextFile(Filename:=fPath, IOMode:=1)        '1:ForReading
        s = inTxt.readAll

        If ignore <> "" Then    '除外文字指定(タイトル行処理)
        '============================================================
            '除外文字列がメタ文字だった場合のエスケープ処理
            reg.Pattern = "([\^\$\?\*\+\.\|\{\}\\\[\]\(\)])"
            ignore = reg.Replace(ignore, "\$1")
            '最初に現れる除外文字(タイトル行)をさがす
            reg.Pattern = "(^" & ignore & ",.*(?=[\n$]))|(\n" & ignore & ",.*(?=[\n$]))"
            '最初のファイルのみ最初に現れる除外文字(タイトル行)をさがす
            sv = ""
            If Not done Then
                Set mt = reg.Execute(s)
                If mt.Count > 0 Then
                    sv = mt(0).Value 'その行を保管
                End If
            End If
            '除外文字列行(タイトル行)の一括削除
            s = reg.Replace(s, "")
        '=============================================================
        End If

        inTxt.Close
        If s <> "" Then outTxt.write sv
        outTxt.write s
        done = True

    Next

    outTxt.Close
    '出力ファイルをエクセルブックとして開き、
    Workbooks.OpenText Filename:=outPath, _
        Origin:=xlWindows, StartRow:=1, DataType:=xlDelimited, TextQualifier:= _
        xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, Semicolon:=False, _
        Comma:=True, Space:=False, Other:=False, TrailingMinusNumbers:=True
   '先頭の改行コードによる空白行の削除
   If ActiveSheet.UsedRange.Row <> 1 Then Rows(1).Delete
   'エクセルブックとして名前を付けて保存。同名のものがあれば無条件上書き。
    Application.DisplayAlerts = False
    ActiveWorkbook.SaveAs Filename:=ThisWorkbook.Path & "\新しいブック名.xlsx", FileFormat:=xlOpenXMLWorkbook
    Application.DisplayAlerts = True
   '出力用テキストファイルの削除
   Kill outPath

   MsgBox "統合完了"

 End Sub

 'ShellApplicationを使った再帰処理
 Private Sub GetFiles(fldItems As Object, dic As Object)
    Dim objFolderItems As Object
    Dim objItem As Object

    For Each objItem In fldItems
        If objItem.isfolder Then
            Set objFolderItems = objItem.GetFolder
            Call GetFiles(objFolderItems.Items, dic)
        Else
            dic(objItem.Path) = True
        End If
    Next

 End Sub

(β) 2016/02/27(土) 14:22


 ↑ 不要になった変数や使わなくなったオブジェクトの生成が残っています。
 がいにはなりませんので無視してください。

(β) 2016/02/27(土) 14:31


 案の定、コードにミスがありました。

 sv = ""

 これは、 If ignore <> "" Then    '除外文字指定(タイトル行処理) の上に移動してください。

 それと、        If s <> "" Then outTxt.write sv

 これは         If sv <> "" Then outTxt.write sv

 にしてください。

(β) 2016/02/27(土) 14:49


β様、ありがとうございます!
UBound(配列,1) 、UBound(配列,2) 、わかり易いご説明、ありがとうございました。

それに、今よりも更にまた目の醒めるようなコード、ありがとうございます!
reg というのが、正規表現の設定ということしか分からないです。。。

これから、再帰処理を理解したあと、統合ファイルにファイル一覧というシートを追加してみて、
ファイル名、件数などのコードを追加する ようなことをやってみようと思います。
その後、Sample4 をステップインさせていただきます!
楽しみが増えました。本当に、ありがとうございました。

(はる) 2016/02/27(土) 14:51


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.