[VBA]ファイル操作

## <i class="fa fa-cubes" style="font-size:1em;"></i> 概要
エクセルのVBAマクロでデータをファイルから読み込みセルに入力する方法
![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/183676/d00917a0-f29b-f99c-2111-fe35386b150b.gif)


こんな感じでエクセルにデータを読み込むコードを書いてみました。
<i class="fa fa-cloud-download" style="font-size:1em;"></i> https://github.com/katsuma99/FileOperation


```vbnet:module1
Option Explicit

'一番下位のファイル/フォルダの文字列を抽出
Public Function GetFileName(ByRef path As String) As String
    Dim fileName As String, pos As Long
    
    '\フォルダー★\ファイル　：★の位置を取得
    pos = InStrRev(path, "\")
    
    '上位パス　と　ファイル　で分割して文字列取得
    fileName = Mid(path, pos + 1)
    path = Left(path, pos - 1)
        
    GetFileName = fileName
End Function

Public Function ReadData(sampleNo As Integer, filePath As String)
    Dim fileName As String              'フォルダ/ファイル
    Dim folderName As String            '拡張子を抜いたファイルパス
    Dim splitPath As String: splitPath = filePath
    
    'フォルダ/ファイル名を抽出
    fileName = GetFileName(splitPath)
    folderName = GetFileName(splitPath)

    '各データを読み込む
    Cells(1, sampleNo) = folderName
    Cells(2, sampleNo) = fileName
    Call ExtractData(filePath, Cells(3, sampleNo))

End Function

Public Function ExtractData(inputPath As String, startCell As Range)
    Dim tmpBuf As String, fno As Long

    'ファイルを開く
    If Dir(inputPath) = "" Then
        MsgBox "データファイルがありません"
        End
    End If
    
    fno = FreeFile
    Open inputPath For Input As #fno

    ' テキストデータチェック
    If EOF(fno) Then
        ' データファイルが不正
        MsgBox "テキストデータが不正です"
        End
    End If
    
    ' データを最後まで読み込む
    Dim offset As Integer: offset = 0
    Do Until EOF(fno)
        Line Input #fno, tmpBuf
        Cells(startCell.Row + offset, startCell.Column) = tmpBuf
        offset = offset + 1
    Loop
    
    Close #fno

End Function

Sub Import()
    Dim filePaths As Variant, fileName As String

    'ファイルマネージャー（起動時は現在開いているのファイルのパス）
    ChDir ThisWorkbook.path
    filePaths = Application.GetOpenFilename(FileFilter:="データファイル(*.txt),*.txt", MultiSelect:=True)

    'キャンセル時終了
    If IsArray(filePaths) = False Then
        End
    End If
    
    'アクティブシートの内容を全て削除
    ActiveSheet.Cells.Clear

    
    '選択されたデータを逐次読み込み
    Dim i As Integer
    For i = 1 To UBound(filePaths)
        Call ReadData(i, CStr(filePaths(i)))
    Next
  
    MsgBox "選択した" & UBound(filePaths) & "ファイルを読み込みました"
End Sub
```


##<i class="fa fa-angle-double-right" style="font-size:1em;"></i> 解説
計測など実験するときに、一旦数値データを以下のようにテキストファイルに書き出し、エクセルで可視化するというパターンがあります。そんな時に、VBAを使ってファイル操作を自動化することで、ファイルを開いてコピペする手間を省くことができます。
![出力された数値データ.PNG](https://qiita-image-store.s3.amazonaws.com/0/183676/f19073db-efd8-450b-f2ad-5bb2b9a6c6f1.png)
    

####<i class="fa fa-list" style="font-size:1em;"></i> 処理のポイント
1. 複数のファイル選択：`Application.GetOpenFilename()`
2. ファイルパスの配列：`filePaths`
3. 文字列の検索：`InStrRev()`
4. ファイルの読み込み：`Line Input`


###<i class="fa fa-cube" style="font-size:1em;"></i> Point1．複数のファイル選択

```vbnet
    'ファイルマネージャー（起動時は現在開いているのファイルのパス）
    ChDir ThisWorkbook.path
    filePaths = Application.GetOpenFilename(FileFilter:="データファイル(*.txt),*.txt", MultiSelect:=True)
```
ChDir : 開くフォルダーのパスを指定
ThisWorkbook.path：開いているエクセルのパス
Application.GetOpenFilename(FileFilter:="**A**，**B**"，MultiSelect:=True)：ファイルマネージャーを開く
　*A：表示名*
　*B：選択できる拡張子*

###<i class="fa fa-cube" style="font-size:1em;"></i> Point2．ファイルパスの配列
```vbnet
    '選択されたデータを逐次読み込み
    Dim i As Integer
    For i = 1 To UBound(filePaths)
        Call ReadData(i, CStr(filePaths(i)))
    Next
```
UBound()：配列のサイズ
CStr()：Stringにキャスト
注意：filePathsの引数は**1**から（0だと有効範囲外エラー）

###<i class="fa fa-cube" style="font-size:1em;"></i> Point3．文字列の検索
```vbnet
Public Function GetFileName(ByRef path As String) As String
    Dim fileName As String, pos As Long
    
    '\フォルダー★\ファイル　：★の位置を取得
    pos = InStrRev(path, "\")
```
ByRef：参照渡し
InStrRev()：文字の検索（**A**，**B**）
　*A：検索の対象となる文字列*
　*B：検索する文字列*
注意：検索は最後の文字から始まり、最初に見つかった位置を返す

###<i class="fa fa-cube" style="font-size:1em;"></i> Point4．ファイルの読み込み
```vbnet
    fno = FreeFile
    Open inputPath For Input As #fno
    
    ' データを最後まで読み込む
    Dim offset As Integer: offset = 0
    Do Until EOF(fno)
        Line Input #fno, tmpBuf
        Cells(startCell.Row + offset, startCell.Column) = tmpBuf
        offset = offset + 1
    Loop
    
    Close #fno
```
流れ：Openでファイルを開いて、Closeでファイルを閉じる
　　Open　**ファイルパス**　For Input As #**ファイル番号**
　　　ファイル操作の処理
　　Close　#**ファイル番号**

FreeFile：使用可能なファイル番号を取得

```vbnet
Do Until EOF(fno)
        Line Input #fno, tmpBuf
```

Do Until EOF(**ファイル番号**)：最後に達するまでループ
Line Input　#**ファイル番号**，**文字列**：ファイルを上から一行ずつ読み込み、文字列として変数に代入


##<i class="fa fa-book" style="font-size:1em;"></i> 参考資料
- [エクセルVBA超入門！たった10分でスタート地点に立つための方法](https://tonari-it.com/vba-start-10minutes/)
- [Office TANAKA - Excel VBA講座：ファイルの操作[目次]](http://officetanaka.net/excel/vba/file/)
- [文字列の長さを取得する(Len/LenB関数)：Excel VBA｜即効テクニック｜Excel VBAを学ぶならmoug](http://www.moug.net/tech/exvba/0140023.html)
- [ExcelVBAマクロでシートを初期化(全てのセルの内容を削除)する - ふらくま日記](http://d.hatena.ne.jp/wild-man/20140412/p1)