Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10全般 : Excel 2013)
複数のcsvファイルを取り込みたい
投稿日時: 20/05/25 10:58:16
投稿者: vaioyuki

いつも大変お世話になっております。
今回はうまくいった!!と思ってもできていませんでした、がっかりパターンです。( ノД`)シクシク…
 
 
ユーザーフォームを使って複数のcsvファイルを選択してそれぞれのシートに貼付したいと思っています。
 

Private Sub Bt0_Click()
Dim buf As String, A As Variant, i As Long, j As Long
Dim r_cnt As Long

Set wsM = Worksheets("マクロ")
Set wsP1 = Worksheets("Print_1")
Set wsP2 = Worksheets("Print_2")
Set wsUser = Worksheets("ユーザ")
Set wsRole = Worksheets("ロール")
Set wsDpt = Worksheets("所属")
Set wsGrp = Worksheets("ユーザ権限")

With wsP1
    Open strFilePath1 For Input As #1
        Do Until EOF(1)
            i = i + 1
            Line Input #1, buf
            A = Split(buf, ",")
            
            For j = 0 To UBound(A)
                .Cells(i, j + 1) = A(j)
            Next j
        Loop
    Close #1

    .Range("A1").CurrentRegion.Copy wsUser.Range("A2")
End With
Stop
With wsP2
    Open strFilePath2 For Input As #1
        Do Until EOF(1)
            i = i + 1
            Line Input #1, buf
            A = Split(buf, ",")
            
            For j = 0 To UBound(A)
                .Cells(i, j + 1) = A(j)
            Next j
        Loop
    Close #1
End With

With wsUser
    r_cnt = .Range("A1").CurrentRegion.Rows.Count
    
    wsP2.Range("A1").CurrentRegion.Copy .Range("A" & r_cnt)
End With

MsgBox "処理完了"

End Sub

Private Sub Bt1_Click()
 strFilePath1 = Application.GetOpenFilename(filefilter:="CSVファイル,*.csv")
 txt1 = strFilePath1
 
    If Dir(strFilePath1) <> "imm_userDB1.csv" Then
        MsgBox "ファイルが違います。「imm_userDB1.csv」を選択してください。"
    End If
End Sub

Private Sub Bt2_Click()
 strFilePath2 = Application.GetOpenFilename(filefilter:="CSVファイル,*.csv")
 txt2 = strFilePath2
 
    If Dir(strFilePath2) <> "imm_userDB2.csv" Then
        MsgBox "ファイルが違います。「imm_userDB2.csv」を選択してください。"
    End If
End Sub


 
「strFilePath2」が貼付されず、
ユーザシートにも「strFilePath1」のデータしか貼付されません。
 
途中で確認してもちゃんと「strFilePath2」の保存先が変数として格納されています。
 
 
全部で2個づつペアで合計8個のcsvファイルを取り込みたいと思っています。(今は錯綜中のためまずは2個)
よろしくお願いします。

投稿日時: 20/05/25 11:13:18
投稿者: vaioyuki

【追記】
ひとつづつ読み込んではいるようですがシートに貼付されません。(´;ω;`)

回答
投稿日時: 20/05/25 13:25:15
投稿者: WinArrow
投稿者のウェブサイトに移動

ユーザーフォームを使わなくても
GetOpenFilename
に複数のファイルを選択するオプション引数がありますから、
試してみては?
 

投稿日時: 20/05/25 15:01:08
投稿者: vaioyuki

ありがとうございます。
 

Sub インストール()
Dim buf As String, A As Variant, i As Long, j As Long
Dim r_cnt As Long

Set wsP1 = Worksheets("Print_1")

With wsP1
    strFilePath1 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv", MultiSelect:=True)

    Open strFilePath1 For Input As #1
        Do Until EOF(1)
            i = i + 1
            Line Input #1, buf
            A = Split(buf, ",")

            For j = 0 To UBound(A)
                .Cells(i, j + 1) = A(j)
            Next j
        Loop
    Close #1
    
End With

End Sub

 
こんな風に書いてみましたが、
 
Open strFilePath1 For Input As #1

 
ここで型が一致しませんとエラーになります。
ちなみにこちらのCSVは全て文字列で読み込みたいのですがそれも私には難しいので貼付先を全て文字列に設定しています。

投稿日時: 20/05/25 15:17:16
投稿者: vaioyuki

もう少し詳しく説明すると、
2つのファイルを1つのシートにまとめます。
 
A1とA2 ⇒ Aシート
B1とB2 ⇒ Bシート
C1とC2 ⇒ Cシート
D1とD2 ⇒ Dシート
 
取り込みCSVファイルを間違えないようにユーザーフォームを使ったときに指定された名前ではない場合のmsgboxを出しました。
(2のファイルはあったりなかったりはします)
最初はいきなりAシートに書き込もうとしたのですが、
そこは私の力不足でw、書き込む際のセルの指定方法がわかりませんでした。
なので隠しシートに一旦書き込んでから各々のシートに書き込む方法にしようと考えました。
 
書き込むシート(Print_1、Print_2)に分けたのも、
Print_1に書き込んだ後次にPrint_2を書きこむ際に指定する方法がわからなかったために2つにシートに分けて、
そこからAシートに書き込もうと思っていました。
 
わかりにくいかもしれませんがよろしくお願いします。

回答
投稿日時: 20/05/25 15:34:22
投稿者: WinArrow
投稿者のウェブサイトに移動

>strFilePath1
変数定義がされていません。
Vriant型で定義します。
GetOpenFiienameメソッドの結果は、配列で格納されます。

投稿日時: 20/05/25 15:47:02
投稿者: vaioyuki

ありがとうございます。
 
Variant型で宣言してるのですがエラーになります。
Variant型で宣言していなかったときは
 

strFilePath1 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv", MultiSelect:=True)

 
ここで型が一致しませんとエラーが出たので変更しました。

回答
投稿日時: 20/05/25 18:18:02
投稿者: WinArrow
投稿者のウェブサイトに移動

参考コード
 
↓を参考に、ご検討ください。
 
Sub test()
Dim CSVFiles, csvfile
Dim Fno As Long
 
    CSVFiles = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv", MultiSelect:=True)
 
    If Not IsArray(CSVFiles) Then Exit Sub
    For Each csvfile In CSVFiles
        Fno = FreeFile
        Open csvfile For Input As #Fno
        Close Fno
    Next
     
End Sub

回答
投稿日時: 20/05/25 19:30:32
投稿者: MMYS

サブルーチン化を検討しましょう。
 

Sub Sample()
    Dim f As Variant
    Dim strFilePath1 As String
    Dim i As Long

    f = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv", MultiSelect:=True)
    If Not IsArray(f) Then Exit Sub
    
    For i = LBound(f) To UBound(f)
        strFilePath1 = f(i)
        LoadFile strFilePath1, Worksheets("Print_1")
    Next

End Sub

Private Sub LoadFile(strFilePath1 As String, wsP1 As Worksheet)
  Dim buf As String, a As Variant, i As Long, j As Long
  With wsP1

    i = .Cells(.Rows.Count, "B").End(xlUp).Row + 1

    Open strFilePath1 For Input As #1
        Do Until EOF(1)
            i = i + 1
            Line Input #1, buf
            a = Split(buf, ",")

            For j = 0 To UBound(a)
                .Cells(i, j + 1) = a(j)
            Next j
        Loop
    Close #1
  End With
  
End Sub

 
 

回答
投稿日時: 20/05/26 10:08:37
投稿者: WinArrow
投稿者のウェブサイトに移動

処理したい内容をよく読んでいなかったので、
簡易買いしていた部分があり、申し訳ありません。
 
話を整理すると
 
4種類のデータが各々2つのCSVファイルの存在しています。
ここでは、1種類づつ処理する前提となっている
2つのCSVファイルの内、1つだけということもある。
・・・ということでよいですね?
 
CSVファイルを指定する場合、1つ目と2つ目が前後してもよいのか
ファイル名で順番が決められるならば、複数選択する方法で処理可能ですが、
人手指定するならば、1つづつ指定することになります。
 
まt、全選択して、取り込むシートが異なるだけならば、本体処理は共通としてサブルーチン化が可能です。
要は、4回の選択操作を、1回にすることができる可能性があるということです。
 
大雑把な流れとしては、↓のようになるでしょう。
4回の選択を行う場合
For Fx1 = 1 to 4
      ファイル選択
     For fx2 = 1 to 2
         取り込み
     Next
Next
 
1回の選択処理
ファイル選択
ファイル名で種類判定&1番目2番目判定
取込み
 
このような整理をした上で、コーディングに向かわないと
後から、いろんな条件が出て、その都度対応を考えることになってしまいます。
作業用シートを使わなくてもよいかもしれません。
 
 
 
 
 
 

投稿日時: 20/05/28 09:25:31
投稿者: vaioyuki

ありがとうございます。
自宅待機中で返事ができずに申し訳ありませんでした。
 
MMYSさん
コメントありがとうございます。
サブルーチン化できました。
何より2行目から貼付したいのに1行目から貼りついてしまい、
どうしたらいいのかと悩んでたところ、
 

i = .Cells(.Rows.Count, "B").End(xlUp).Row + 1

 
これで頭の中ですごく前進しました。
 
 
WinArrowさん
いつもありがとうございます。
そうなんです。4回同じことをしなくてはいけないんです。
そして間違い防止としてファイル名の確認をしたいのでフォームで選択してから。。。と考えていました。
 
 
 
今はBATファイルで先に2つのファイルを1つにまとめて、
出来上がった4つのファイルを選択するようにして貼付出来たらと考えています。
視覚的にはサブフォームのほうがわかりやすい気がするのですが、
やはり使わないほうがコード的には楽なのでしょうか?(^^;)

投稿日時: 20/05/28 11:54:15
投稿者: vaioyuki

懲りずに。。。w
 
フォームで考えてみましたが配列がありませんと出ます。
 

Public strFilePath1 As String
Public strFilePath2 As String
Public strFilePath3 As String
Public strFilePath4 As String

Dim wsM As Worksheet
Dim wsP1 As Worksheet
Dim wsUser As Worksheet
Dim wsRole As Worksheet
Dim wsDpt As Worksheet
Dim wsGrp As Worksheet

Private Sub Bt0_Click()
Dim buf As String, a As Variant, i As Long, j As Long, s As Variant
Dim r_cnt As Long

Set wsM = Worksheets("マクロ")
Set wsP1 = Worksheets("Print_1")
Set wsUser = Worksheets("ユーザ")
Set wsRole = Worksheets("ロール")
Set wsDpt = Worksheets("所属")
Set wsGrp = Worksheets("ユーザ権限")

With wsP1
    
    For s = "1" To "4"
        Open strFilePath(s) For Input As #1
            Do Until EOF(1)
                i = i + 1
                Line Input #1, buf
                a = Split(buf, ",")
            
                For j = 0 To UBound(a)
                    .Cells(i, j + 1) = a(j)
                Next j
            Loop
        Close #1
        
        If sfp = 1 Then
            .Range("A1").CurrentRegion.Copy wsUser.Range("A2")
        ElseIf sfp = 2 Then
            .Range("A1").CurrentRegion.Copy wsRole.Range("A2")
        End If
        .Range("A1").CurrentRegion.ClearComments
    Next s
    
End With

MsgBox "処理完了"

End Sub

Private Sub Bt1_Click()
 strFilePath1 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv")
 txt1 = strFilePath1
 
    If Dir(strFilePath1) <> "imm_userDB.csv" Then
        MsgBox "ファイルが違います。「imm_userDB.csv」を選択してください。"
    End If
End Sub

Private Sub Bt2_Click()
 strFilePath2 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv")
 txt2 = strFilePath2
 
    If Dir(strFilePath2) <> "b_m_account_role_bDB.csv" Then
        MsgBox "ファイルが違います。「b_m_account_role_bDB.csv」を選択してください。"
    End If
End Sub

 
wsP1シートに張り付けていって、
そこから各々のシートに貼付しに行く方法をとろうと思いました。(とりあえずは2つ)
 
Open strFilePath(s) For Input As #1

 
こちらではstrFilePath1を見に行っているようですが「配列がありません」とエラーになります。
 
よろしくお願いします。

回答
投稿日時: 20/05/28 21:50:12
投稿者: MMYS

掲示されているコードではエラーになるばす。
原因はコンパイルすれば気づくと思います。
 
「ツール」→「VBA Projectのコンパイル」
 
また、次のコードの先頭に下記を入れて、
 
Option Explicit
 
再度、「VBA Projectのコンパイル」を実行してみて下さい。
特に、Option Explicit は必ずしましょう。
 
 
 
> For s = "1" To "4"
> Open strFilePath(s) For Input As #1
 
配列を正しく理解してますか。
前回の私の投稿で、変数 i はLong型ですが、なぜLong型なのでしょう。
 
    Dim i As Long
    For i = LBound(f) To UBound(f)
 
 
また前回、サブルーチン化を提案しましたが、それはパラメータを与えれば
あとは勝手に処理してくれる。つまり、パラメータだけ考えれば良いのです。
例えば、LoadFileルーチンは 下記のようになります。

'目的
' CSVファイルを指定したワークシートに読み込む
'バラメータ
' strFilePath1 処理するファイル名をフルパスで指定する
' wsP1     内容を書き込むワークシートを指定する

'
Private Sub LoadFile(strFilePath1 As String, wsP1 As Worksheet)
  '呼び出し元では内部処理のことは考えない。
  '呼ぶだけで、勝手に処理してくれる。
End Sub
 

投稿日時: 20/05/29 13:13:37
投稿者: vaioyuki

コメントありがとうございます。
 
コンパイルしてみましたが何も起こりませんでした。
先頭行には
 
Option Explicit
For s = "1" To "4"
が自動でつくように設定しています。
 
 

For s = "1" To "4"

 
このように表記したのは普通に「1 to 4」でも駄目だったのでもしかして文字としてでないと認識しないため?と思いダブルコーテーションをつけました。
 
配列を正しく理解しているかと言われれば理解していません。
以前使っていたものでは前任者がarray関数でユーザーフォームを使ってCSVファイルを読み込んでいた記憶があるのですが、まったく思い出せずにいます。
 
iは行番号だと思っています。
aはわかっていません。
 
すごく単純な考えかもしれませんが、
 
Public strFilePath As Variant

 
から「strFilePath(s)」で(s)が1〜4に変わってくれるのかなと思ってました。
なので「型が一致しません」と出てもVariantにしてもダメなので何かほかに原因があるのかなと。
 
 
そこまで難しいことをしようとしているのかなとちょっとくじけそうです。。。

回答
投稿日時: 20/05/29 17:37:11
投稿者: MMYS

vaioyuki さんの引用:

コンパイルしてみましたが何も起こりませんでした。

開示してないコードがあるのですか。
vaioyukiさんが 投稿日時: 20/05/28 11:54:15
に開示したコードではエラーです。
 
strFilePath() はどこで定義されてますか。
開示されたコードに定義がありません。
 
For s = "1" To "4" とありますが、
変数 s も定義されていません。
どこで定義されてますか。
 
未定義ならコンパイルエラーです。
 
 

投稿日時: 20/05/29 18:08:59
投稿者: vaioyuki

ありがとうございます。
 
ユーザーフォームに書いたコードは以下になります。
 

Option Explicit

Public strFilePath As Variant
Public strFilePath1 As Variant
Public strFilePath2 As Variant
Public strFilePath3 As Variant
Public strFilePath4 As Variant

Dim wsM As Worksheet
Dim wsP1 As Worksheet
Dim wsP2 As Worksheet
Dim wsUser As Worksheet
Dim wsRole As Worksheet
Dim wsDpt As Worksheet
Dim wsGrp As Worksheet

Private Sub Bt0_Click()
Dim buf As String, a As Variant, i As Long, j As Long, s As Variant
Dim r_cnt As Long

Set wsM = Worksheets("マクロ")
Set wsP1 = Worksheets("Print_1")
Set wsUser = Worksheets("ユーザ")
Set wsRole = Worksheets("ロール")
Set wsDpt = Worksheets("所属")
Set wsGrp = Worksheets("ユーザ権限")

With wsP1
    
    For s = 1 To 4
        Open strFilePath(s) For Input As #1
            Do Until EOF(1)
                i = i + 1
                Line Input #1, buf
                a = Split(buf, ",")
            
                For j = 0 To UBound(a)
                    .Cells(i, j + 1) = a(j)
                Next j
            Loop
        Close #1
        
        If s = 1 Then
            .Range("A1").CurrentRegion.Copy wsUser.Range("A2")
        ElseIf s = 2 Then
            .Range("A1").CurrentRegion.Copy wsRole.Range("A2")
        End If
        .Range("A1").CurrentRegion.ClearComments
    Next s
    
End With

MsgBox "処理完了"

End Sub

Private Sub Bt1_Click()
 strFilePath1 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv")
 txt1 = strFilePath1
 
    If Dir(strFilePath1) <> "imm_userDB.csv" Then
        MsgBox "ファイルが違います。「imm_userDB.csv」を選択してください。"
    End If
End Sub

Private Sub Bt2_Click()
 strFilePath2 = Application.GetOpenFilename(FileFilter:="CSVファイル,*.csv")
 txt2 = strFilePath2
 
    If Dir(strFilePath2) <> "b_m_account_role_bDB.csv" Then
        MsgBox "ファイルが違います。「b_m_account_role_bDB.csv」を選択してください。"
    End If
End Sub


 
よろしくお願いします。

回答
投稿日時: 20/05/30 00:00:39
投稿者: MMYS

Public strFilePath1 As Variant
Public strFilePath2 As Variant
 
strFilePath1 と strFilePath2 は別の変数。理解してますよね。
 
 
Public strFilePath As Variant
Public strFilePath1 As Variant
 
strFilePath と strFilePath1 も別の変数。理解してますよね
 
 
 
strFilePath1 は、Bt1_Clickで値を設定してます。
strFilePath2 は、Bt2_Clickで値を設定してます。
 
strFilePath はどこで値を設定してますか。

回答
投稿日時: 20/05/30 20:26:17
投稿者: WinArrow
投稿者のウェブサイトに移動

投稿日時: 20/05/29 18:08:59
で掲示したコードは、ただのコードの羅列であって、支離滅裂
 操作の手順との関連がわかりません。
  
単純に考えて
4つのボタンを用意し、
ボタンを売りックしたときに
 ボタンに対応したCSVファイルを読込、対応したシートにとりこむ
 ということだと思います。
  
話を整理するために、↓のような表を作成してみてください。
ボタン CSVFiLE       シート名
BTN1 imm_userDB.csv      ユーザ
BTN2  b_m_account_role_bDB.csv ロール
BTN3            所属
BTN4            ユーザ権限
  
ここでわかること
 ・CVSファイル名と取込みシート名を変数化すれば、CSVデータとシートに複写するロジックは同じである。
  
コード作成に先走るのではなく、
このような整理をすると、矛盾したコードになることはない
 と思います。
 
表の作成方法のサンプルコード
【標準モジュール】
Public Type tTbl
    BtnName As String
    CSVFILE As String
    SheetName As String
End Type
 
【Userform1】
Dim TBLdata As Range
Dim hName(1 To 4) As tTbl
Dim s As Long
 
Private Sub UserForm_Initialize()
    Set TBLdata = ThisWorkbook.Sheets(1).Range("A2:C5")
    For s = 1 To 4
        With hName(s)
            .BtnName = tbdata(s, 1).Value
            .CSVFILE = TBLdata(s, 2).Value
            .SheetName = TBLdata(s, 3)
        End With
    Next
             
End Sub
 
 
 

回答
投稿日時: 20/05/30 20:55:34
投稿者: WinArrow
投稿者のウェブサイトに移動

コマンドボタンクリック&取込処理の参考コード
 
【Useform1】
Private Sub BTN1_Click()
    With hName(1)
        Call 取込処理(CSVFILE:=.CSVFILE, Sheet:=Worksheet(.SheetName))
    End With
End Sub
 
Private Sub 取込処理(ByVal CSVFILE As stin, Sheet As Worksheet)
Dim FNO As ling, BUF As String, Rx As Long
 
    FNO = FreeFile
    Open Me.FolderName.Text & "\" & CSVFILE For Input As #FNO
    Do Until EOF(FNO)
    Line Input #FNO, BUF
    With Sheet
        .Range("A" & .Rows.Count).End(xlUp).Offset(1).Value = BUF
    End With
    Loop
    Close FNO
     
    With Sheet
        .Columns("A").TextToColumns _
        Destination:=Range("A1"), _
        DataType:=xlDelimited, _
        Comma:=True
    End With
End Sub

投稿日時: 20/05/31 18:09:52
投稿者: vaioyuki

ありがとうございます。(;▽;)
本当にここの掲示板には感謝しかないです。
ただ今の会社がここに書き込もうとするとはじかれてしまい、
何度も何度もログインを繰り返してやっと書き込める状態なのでなかなか感謝が伝えられずに申し訳ないです。
 
 
MMYSさん
ありがとうございます。
そうなんです。お恥ずかしながらそこが自分でも全くわからないとこなんです。
 
Public strFilePath As Variant
 
これは strFilePath(s) を使うために必要なのかなと考えました。
パスに変数を持たせるためにはどうしたらいいのだろうと考えました。
 
 
WinArrowさん
いつもありがとうございます。
コードの羅列、支離滅裂、確かに私の今の力で考えられることを並べただけのコードです。
 
今は
[text1][Bt1]
[text2][Bt2]
[text3][Bt3]
[text4][Bt4]
とパス名を表示するテキストと参照ボタンを4つ設けていて、
[Bt0]ボタンで取り込みボタンにしています。
 
今は自宅なので試すことが出来ませんが、
また報告させて頂きます。

回答
投稿日時: 20/06/01 07:42:50
投稿者: WinArrow
投稿者のウェブサイトに移動

引用:
今は
[text1][Bt1]
 [text2][Bt2]
 [text3][Bt3]
 [text4][Bt4]

なうほど・・・
  
仮に、4種類のCSVファイルが同一フォルダ内に存在数としたら、
4つのアイルを[GetOpenFilename]でファイルパスを取得するのは無駄。
フォルダ名だけ取得すれば、ファイル名部分は、プログラムで補完できますよね?
なぜならば、指定のファイル名と違ったらエラーにしているわけですから、ファイル名は
固定なんだと思うからです。
Btn0そのものをサブルーチンとして、各々のボタンクリックプロシジャから呼び出せばよいでしょう。
前レスの参考コードを参照
 
#何もボタンを押させなくてもよい
 

投稿日時: 20/06/08 16:39:32
投稿者: vaioyuki

ありがとうございます。
 
1週間別件で忙しくて作業が全く進みませんでした。
そして、
私の頭では難しすぎました。
うっすら記憶でユーザフォームのボタンに番号が振られてそれを繰り返すとそのパスを読みに行くようなことをやってたような気がするのでできるのではと思いましたが私には難しいようです。
WinArrowさんが書いてくださったコードもどういった状況で動くのか、
エラーが起きても何でエラーが起きるのかもわかりません。
 
色々検索もするのですが、
CドライブとかDドライブを指定できればいいのですが、
会社のパソコンになりますので最初にユーザIDが必要になるのでそれをどう変換していいのかもわかりません。
変数としてパスを持たせて。。。と考えたのですがうまくいきませんでした。
 
誤って違うファイルを取り込まないようにしたかったのですが、
4ついっぺんに取り込むことは諦めます。
もう少し勉強します。
ありがとうございました。