大量のテキストファイルをエクセルに読み込む(高速版)

解析結果として得られた大量のテキストファイルを高速で1つのエクセルファイルに読み込むマクロを作る

スポンサーリンク

目的とフローチャート

今回の目的は,前回(>XFLR5で二次元翼を解析して結果を出力する)で出力した大量のテキストファイルを1つのエクセルファイルに読み込むことである

テキストファイルの中身はこんな感じ

最初の数行にファイルの説明があって,それ以下にスペース区切りでデータが入力されている

xflr5 v6.43

 Calculated polar for: DAE31

 1 1 Reynolds number fixed          Mach number fixed         

 xtrf =   1.000 (top)        1.000 (bottom)
 Mach =   0.000     Re =     0.100 e 6     Ncrit =   9.000

  alpha     CL        CD       CDp       Cm    Top Xtr Bot Xtr   Cpmin    Chinge    XCp    
 ------- -------- --------- --------- -------- ------- ------- -------- --------- ---------
  -9.800  -0.2128   0.13213   0.12747  -0.0500  0.9789  0.0569  -1.3124   0.0000  -0.0078
  -9.700  -0.2097   0.13112   0.12646  -0.0507  0.9774  0.0575  -1.3107   0.0000  -0.0147
  -9.600  -0.2060   0.13007   0.12541  -0.0517  0.9760  0.0581  -1.3141   0.0000  -0.0244
  -9.500  -0.2023   0.12906   0.12440  -0.0532  0.9747  0.0586  -1.3268   0.0000  -0.0365
  -9.400  -0.1991   0.12813   0.12348  -0.0551  0.9736  0.0589  -1.3504   0.0000  -0.0503
  -9.300  -0.1970   0.12709   0.12245  -0.0552  0.9719  0.0591  -1.3435   0.0000  -0.0537
  -9.200  -0.1920   0.12580   0.12116  -0.0554  0.9705  0.0596  -1.3205   0.0000  -0.0626
  -9.100  -0.1870   0.12461   0.11997  -0.0561  0.9692  0.0601  -1.3065   0.0000  -0.0745
  -9.000  -0.1817   0.12345   0.11880  -0.0571  0.9680  0.0608  -1.3019   0.0000  -0.0896
  -8.900  -0.1764   0.12228   0.11764  -0.0586  0.9670  0.0616  -1.3054   0.0000  -0.1077
    :       :        :         :         :       :       :        :        :        :    
  18.900   1.6401   0.11388   0.10755  -0.0790  0.0766  1.0000  -6.1234   0.0000   0.2624
  19.000   1.6434   0.11462   0.10825  -0.0791  0.0749  1.0000  -6.1735   0.0000   0.2620
  19.100   1.6363   0.11708   0.11086  -0.0801  0.0740  1.0000  -6.1546   0.0000   0.2626
  19.200   1.6338   0.11880   0.11265  -0.0808  0.0727  1.0000  -6.1657   0.0000   0.2628
  19.300   1.6412   0.11885   0.11260  -0.0805  0.0708  1.0000  -6.2426   0.0000   0.2621
  19.400   1.6331   0.12152   0.11543  -0.0817  0.0702  1.0000  -6.2143   0.0000   0.2629
  19.500   1.6277   0.12376   0.11779  -0.0828  0.0693  1.0000  -6.2035   0.0000   0.2634
  19.600   1.6412   0.12274   0.11658  -0.0818  0.0671  1.0000  -6.3208   0.0000   0.2622
  19.700   1.6319   0.12566   0.11967  -0.0833  0.0666  1.0000  -6.2826   0.0000   0.2631
  19.800   1.6237   0.12842   0.12259  -0.0848  0.0661  1.0000  -6.2505   0.0000   0.2640
  19.900   1.6176   0.13087   0.12515  -0.0861  0.0654  1.0000  -6.2310   0.0000   0.2648
  20.000   1.6190   0.13194   0.12623  -0.0865  0.0643  1.0000  -6.2641   0.0000   0.2647

Excelファイルとテキストデータは同じディレクトリにおいてあり,Re数が100,000から1,000,000までのデータが20,000おきに出力されている

この手法は,学生実験などの結果がテキストファイルで与えられたときなどにも役立つと思うのでぜひ参考にしてほしい

マクロのフローチャートを以下に示す

  1. 変数を宣言する
  2. 必要な値をシートから読み込む
  3. 読み込んだ値からテキストファイルのファイル数を計算し,ループを始める
  4. 読み込みたいファイル名を生成する
  5. テキストファイルを開く
  6. テキストファイルを1行ずつ読み込む
  7. 1行を数値ごとに分割して配列に格納する
  8. さらにループを回してすべての必要なデータを配列に格納する
  9. ファイルを閉じる
  10. すべてのファイルについて上の処理が終われば,配列をエクセルシートに貼り付ける

プログラムの解説

それでは実際に解説していく

プログラム全文は以下の通り

Sub ReadTextFile()

'変数の宣言
Dim file_number As Integer 'ファイル数
Dim data_number As Integer 'データ数
Dim Re_min  As Double '最小Re数
Dim Re_max As Double '最大Re数
Dim Re_delta As Double 'Re数刻み
Dim foil_name As String '翼型名
Dim file_name As String 'ファイル名
Dim data(30000, 10) As Double 'データ格納用配列
Dim data_tmp As Variant
'カウンター
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim n As Integer
'タイマーの変数
Dim startTime As Double
Dim endTime As Double
Dim processTime As Double

'ActiveWorkbook.Save'まず上書き保存
Application.ScreenUpdating = False '画面更新の非表示
Application.DisplayAlerts = False '警告画面の非表示
startTime = Timer '開始時間取得

'値の読み込み
Re_min = Cells(1, 4) '最小Re数
Re_max = Cells(1, 6) '最大Re数
Re_delta = Cells(1, 8) 'Re数刻み
foil_name = Cells(1, 2) '翼型名

'ファイル数の計算
file_number = (Re_max - Re_min) / Re_delta + 1

For i = 0 To file_number - 1 'すべてのファイルのループ
    '開きたいファイル名を作る
    file_name = foil_name & "_T1_Re" & Format((Re_min + Re_delta * i) / 1000000, "0.000") & "_M0.00_N9.0.txt"
    Open ThisWorkbook.Path & "\" & file_name For Input As #1 'txtファイルを読込専用で開く
        j = 0
        Do Until EOF(1) 'txtファイルの終わりまでのループ
            Line Input #1, buf '1行ごとに読み込み
            If (j > 10 And buf <> "") Then
                data_tmp = Split(buf, " ") '文字列を空白で分割
                num = 0
                For k = 0 To UBound(data_tmp, 1)
                    If (data_tmp(k) <> "") Then '空白を飛ばして読み込み
                        data_tmp(num) = data_tmp(k)
                        num = num + 1
                    End If
                Next k
                '文字列からDoubleに変換する
                data(data_number, 0) = Val(data_tmp(0)) 'alpha
                data(data_number, 1) = Val(data_tmp(1)) 'Cl
                data(data_number, 2) = Val(data_tmp(2)) 'Cd
                data(data_number, 3) = Val(data_tmp(4)) 'Cm
                data(data_number, 4) = Re_min + Re_delta * i 'Re
                data_number = data_number + 1
            End If
            j = j + 1
        Loop
    Close #1
    
    Application.StatusBar = "反復回数" & i & "/" & file_number - 1 'ステータスバーに反復回数を表示
    DoEvents
Next i
Application.StatusBar = False

Range(Cells(2, 1), Cells(2 + data_number - 1, 5)) = data()

'終了時間取得
endTime = Timer

'処理時間表示
processTime = endTime - startTime
MsgBox "計算が終了しました" & vbCrLf & "処理時間:" & processTime

End Sub

使用する変数

マクロに使用する変数を以下に示す

'変数の宣言
Dim file_number As Integer 'ファイル数
Dim data_number As Integer 'データ数
Dim Re_min  As Double '最小Re数
Dim Re_max As Double '最大Re数
Dim Re_delta As Double 'Re数刻み
Dim foil_name As String '翼型名
Dim file_name As String 'ファイル名
Dim data(30000, 10) As Double 'データ格納用配列
Dim data_tmp As Variant
'カウンター
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim n As Integer
'タイマーの変数
Dim startTime As Double
Dim endTime As Double
Dim processTime As Double

データ数data_numberは最後に配列をシートに貼り付けるときのみ使用

データ格納用配列data()を動的配列にしてもよかったが,面倒なのでやめた.データ数は30000もあれば十分だろう

ほしいデータの種類はα,CL,CD,Cm,Reなので,余裕をもってdata(0:30000, 0:10)で宣言した

前準備

(>Excel VBA/マクロ について知っておいてほしいこと)でも言ったとおり,マクロの操作は戻るボタンで取り消せないので,壊滅的な被害を防ぐためにプログラムの最初に次のコードを入れておく

ActiveWorkbook.Save '上書き保存
Application.ScreenUpdating = False '画面更新の非表示
Application.DisplayAlerts = False '警告画面の非表示

警告画面というのは,Excelファイルを閉じるときの「変更内容を保存しますか?」みたいなやつのこと

値の読み込み・ファイル数の計算

まず,今回のマクロに必要な値をシートから読み込む

'値の読み込み
Re_min = Cells(1, 4) '最小Re数
Re_max = Cells(1, 6) '最大Re数
Re_delta = Cells(1, 8) 'Re数刻み
foil_name = Cells(1, 2) '翼型名

読み込みに使うのはRangeでもCellsでもいいが,両方使えるときはCellsのほうが少し早いらしい

解析に使用した最小Re,最大Re,およびRe数の刻みから,解析結果のファイル数は次のように計算できる

'ファイル数の計算
file_number = (Re_max - Re_min) / Re_delta + 1

ファイルのループ開始

ファイルのループを開始する

data_number = 0
For i = 0 To file_number - 1 'すべてのファイルのループ
	(各ファイルについての処理)
next i

データ数data_numberは,配列に格納しながら数えていくので,ループが始まる前に初期値の0を入れておく

ファイル名の生成

それぞれのファイルについて,まず開きたいファイル名を生成する

i番目のファイルのファイル名をfile_nameに格納するコードは次のようになる

'開きたいファイル名を作る
file_name = foil_name & "_T1_Re" & Format((Re_min + Re_delta * i) / 1000000, "0.000") & "_M0.00_N9.0.txt"

&は複数の文字列の結合で,Format(数値,書式)を使えば,指定した桁数で数値を文字列に変換できる

ファイルを開く/閉じる

ファイルを開くにはOpenを使い,ファイルがある場所(パス)とファイル名を指定する必要がある

Open ThisWorkbook.Path & "\" & file_name For Input As #1 'txtファイルを読込専用で開く

ThisWorkbook.Pathはむちゃくちゃ便利な奴で,マクロが書かれているエクセルシートと同じ場所を指定してくれる

こうすれば,だれがどのパソコンで使おうと,2つのファイルが同じフォルダにありさえすればいいので使いやすい

ファイルを閉じるには次のコードを使えばいい

Close #1

エクセルシートを置いているフォルダをOneDriveと同期している場合はThisWorkbook.Pathが使えないのでこちらを参考
[VBA]OneDriveで同期しているファイルまたはフォルダのURLをローカルパスに変換する関数

OneDriveUrlToLocalPath(ByRef Url As String)

テキストファイルを行ごとに読み込む

行ごとにテキストファイルのデータを読み込んでいく

j = 0
Do Until EOF(1) 'txtファイルの終わりまでのループ
    Line Input #1, buf '1行ごとに読み込み
    If (j > 10 And buf <> "") Then
        data_tmp = Split(buf, " ") '文字列を空白で分割
        num = 0
        For k = 0 To UBound(data_tmp, 1)
            If (data_tmp(k) <> "") Then '空白を飛ばして読み込み
                data_tmp(num) = data_tmp(k)
                num = num + 1
            End If
        Next k
        '文字列からDoubleに変換する
        data(data_number, 0) = Val(data_tmp(0)) 'alpha
        data(data_number, 1) = Val(data_tmp(1)) 'Cl
        data(data_number, 2) = Val(data_tmp(2)) 'Cd
        data(data_number, 3) = Val(data_tmp(4)) 'Cm
        data(data_number, 4) = Re_min + Re_delta * i 'Re
        data_number = data_number + 1
    End If
    j = j + 1
Loop

行数がわからないテキストファイルを読み込むには, Do Until EOF(1) ~ Loopを使う

ファイルの最終行(End of File:EOF)に到達すると自動的にループを抜けてくれる

If (j > 10 And buf <> "") Then ~ End Ifj = j + 1 で,テキストファイル冒頭の説明の部分の読み込みをスキップしている

テキストファイルを行ごとに読み込むには, Line Inputを使う.読み込み先のファイルとして#1を指定し,読み込んだ行は文字列としてbufに格納される

Line Input #1, buf によって,bufには次の文字列が格納されている

"  -9.800  -0.2128   0.13213   0.12747  -0.0500  0.9789  0.0569  -1.3124   0.0000  -0.0078"

この文字列を data_tmp = Split(buf, " ") の行で空白区切りにしてdata_tmpに格納している

この時点では,data_tmpの配列の中には次のような空の要素""が混じってしまっている

この空の要素は For k = 0 To UBound(data_tmp, 1) ~ Next k で取り除かれている

最後はVal()を使って文字列を数値に変換して出力用配列data()に格納している

値の貼り付け

すべてのファイルについてのループを終了すれば,最後にデータをエクセルシートに貼り付ける

'値の貼り付け
Range(Cells(2, 1), Cells(2 + data_number - 1, 5)) = data()

配列の貼り付けはこれでできる
くれぐれもセル1つずつに数千回貼り付けを行うようなことはしないように

これでコードは終了である

プログラムの実行

今回作成したプログラムを実際に実行してみる

46個のファイルを読み込んで実行時間は約0.5秒だった

まとめ

2年前に書いた記事のプログラムと比べて,実行時間を50倍以上高速化することができた

上の説明でわからないことがあればグーグルで検索してみよう.きっと答えはそこにあるはず

コメント

タイトルとURLをコピーしました