GetPrivateProfileString関数は遅い
INIファイルの取得にGetPrivateProfileString関数を使うことがあります。
ただ、GetPrivateProfileString関数にはいくつかの問題があります。
- 処理が遅い。
- 値取得用の変数を事前に拡張しておく必要がある。
- 値取得後に事前に拡張した領域のうち不要な部分を除去する必要がある。
- おまけ。Shift-JIS以外のファイルは文字化けする。
対応方法はあります。
INIファイルを自分で読み込んで内容を取得する
処理が遅い点については改善点はあります。INIファイルを事前に全てメモリに格納しておき、処理中に必要になったらメモリを参照する、という方法です。メモリ、と書きましたが、実際にはINIファイル用の構造体(ユーザー定義型)にしておくと使い勝手がよいです。
ただし、それだけでは処理が遅い点以外の問題には対応できません。上記の問題を全て対応するには、そもそもGetPrivateProfileString関数を使わない、という方法を採ります。
具体的には、自分でファイルを読み込んで、キーと値の組み合わせを連想配列に格納し、最終的に構造体に格納する、という方法です。
この方法はINIファイルに設定されている内容が多ければ多いほど、高速化の恩恵を受けます。この記事はこの2点目がメインの内容になります。
なお、INIファイルは一般的にはShift-JISの場合が多いので、以下のサンプルでもINIファイルはShift-JISとしています。UTF-8などの場合は、ADODB.Streamを使ってファイルを参照する必要があります。
その辺りについては「VBAでUTF-8のファイルをShift-JISに変換する」をご参照ください。
処理手順の概要
高速にINIファイルから値を取得するための手順概要は以下になります。
- INIファイルを開く。
- 1行ずつ読み込む。
- キーと値の行であれば内容を連想配列に格納する。
- INIファイルを閉じる。
- 構造体に登録する内容を1件ずつ連想配列から取得する。
サンプルのINIファイル
以下のサンプルコードで使うINIファイルは以下の構成とします。
セミコロンはコメント行で、セクションが2つあり、それぞれで設定が2つずつあり、空行もある想定です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
;//------------------------------------------ ;// ファイル名:tcp.ini ;// 内容 :TCP処理に必要な設定を保持する ;//------------------------------------------ ;// TCPサーバ情報 [TCP_SERVER] IP=192.168.20.1 PORT=12345 ;// TCPクライアント情報 [TCP_CLIENT] IP=192.168.11.5 PORT=50005 |
事前設定
以下のコードでは連想配列のDictionaryクラスを利用しています。
Dictionaryクラスを利用するためには、VBA画面のツールメニュー→参照設定を選び、参照設定ダイアログで「Microsoft Scripting Runtime」にチェックを付けます。
サンプルコード
上記のINIファイルを読み込み、最終的には構造体に格納するまでの流れのサンプルコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Type ST_INI Server_IP As String Server_PORT As String Client_IP As String Client_PORT As String End Type Sub IniToDictionary() Dim sIniPath '// INIファイルのパス Dim fn '// ファイル番号 Dim sLine '// ファイル読み込み行 Dim sLeftChar '// 左端の文字 Dim sSection '// セクション Dim sKey '// キー Dim sValue '// 値 Dim v '// キーと値 Dim dic As New Dictionary '// INIファイル登録Dictionary Dim t As ST_INI '// INIファイル構造体 '// INIファイルのパスを設定 sIniPath = "C:\test\tcp.ini" fn = FreeFile Open sIniPath For Input As #fn '// ファイル行ループ Do Until EOF(fn) '// 1行読み込み Line Input #fn, sLine '// 空行の場合は次行へ If Len(sLine) = 0 Then GoTo CONTINUE End If '// 左端の文字を取得 sLeftChar = Left(sLine, 1) '// コメント行の場合は次行へ(左端が;の場合) If sLeftChar = ";" Then GoTo CONTINUE '// セクション行の場合(左端が[の場合) ElseIf sLeftChar = "[" Then '// セクション文字列を取得 sSection = Mid(sLine, 2, Len(sLine) - 2) GoTo CONTINUE '// データ設定行の場合(=を含む場合) ElseIf InStr(1, sLine, "=") > 0 Then v = Split(sLine, "=") sKey = v(0) sValue = v(1) End If '// Dictionaryに登録(セクション+キー、値) Call dic.Add(sSection & sKey, sValue) CONTINUE: Loop Close #fn '// 構造体の各項目にDictionaryから値を取得する t.Server_IP = dic.Item("TCP_SERVER" & "IP") t.Server_PORT = dic.Item("TCP_SERVER" & "PORT") t.Client_IP = dic.Item("TCP_CLIENT" & "IP") t.Client_PORT = dic.Item("TCP_CLIENT" & "PORT") End Sub |
コード説明
各処理はコメントを書いているので分かると思いますので、考え方を説明します。
まず、INIファイルを開いて1行ずつ読み込んでいきます。
読み込んだ1行がセクション行ならセクション文字列を取得し、キーと値の行ならキー文字列と値文字列を取得します。
キーと値を取得したら、Dictionaryに追加します。
追加する際のDictionaryのキーには、INIファイルのセクションとキーを連結した文字列を指定しています。
ここまでがINIファイルをDictionaryに登録する処理です。
あとは、INIファイルの項目と対になる構造体の各項目にDictionaryのItemメソッドで値を設定します。
Itemメソッドに渡す文字列はセクション文字列とキー文字列を連結させたものになります。
あとはINIファイルの内容が必要になった処理で、構造体の対象項目を参照していくことになります。