外部のテキスト形式ファイル(CSV、TXT、TAB、ASC など)に接続/操作する手段は用途に応じて複数用意されています。これらを熟知していれば、テキスト形式ファイルとの連携に関しての障害はほとんどありません。しかし、接続方法個々に特徴や制限があり、その多様性自体が初心者を混乱させかねないのも、また事実です。
ここでは外部テキスト形式ファイルに接続/操作する手段を整理して、用途に応じて適切な手段を選択できるような指針を提供します。
※ 以下で紹介する各種メソッドについては、ヘルプに詳しい説明や使用例が載っています。本トピックはそれら各種接続手段の位置づけを明確にするものであり、個々の使用法詳細をヘルプに代わって解説するものではありません。
オプション値などについては省略している場合がありますので、各メソッドの詳細については、必ずヘルプを当たってください。
また筆者自身の不明により、ADO での操作は一部を除き取り扱っていません。DAO 中心です。
可能な操作 | |
---|---|
Shell | 起動のみ |
DoCmd.OutputTo | |
DoCmd.TransferText | |
SQL 文 | |
OpenRecordset | |
Open | |
Open, Line Input #, Print # | |
FileSystemObject | |
API |
外部テキストファイルを開くだけで何も操作をしなくてよい場合は、もっとも簡単なのがこの Shell 関数です。
Call Shell("NOTEPAD C:\Temp.txt", 1)
Shell 関数自体は別にテキストファイルを操作するための関数ではなく、単にアプリケーションを呼び出すだけで、呼び出し先はメモ帳でも秀〇でも Excel でも何でもかまいません。上記例ではコマンドライン オプションでテキストファイルのフルパスを渡すことによって、メモ帳に起動と同時にテキストファイルを開かせています。
なおテキストファイルのフルパス中に半角スペース文字が混じる場合は、下記のようにパスをさらに二重のダブル クォーテーションで括る必要があります。
Call Shell("NOTEPAD ""C:\Documents and Settings\YU-TANG\デスクトップ\Temp.txt""", 1)
アプリケーションを指定せずに拡張子の関連付けに応じて適切なアプリケーションで実行させたい場合は、DOS の START コマンドや Windows API の ShellExecute 関数を使う方法もあります。
テキストファイルはもっとも基本的なファイル形式であるため、テキストファイルの操作に関しては DOS コマンド (COPY など) で事足りてしまう場合も少なくありません。
DOS コマンドについては、論を外れるので(詳しくないだけ、という話も^ ^;)踏み込みません。興味があれば書籍や Web サイトを当たってください。
オブジェクトをエクスポートします。
DoCmd.OutputTo acOutputTable, "tblData", _
acFormatTXT, "C:\Exported.txt", True
第 4 引数の出力ファイル名はフルパスで記述します。何か特殊な理由が無い限り、マッピングされたドライブ名(例 >> Q:\SheredFolder\Temp.txt)ではなく、UNC パス(例 >> \\ServerName\SheredFolder\Temp.txt)を使用した方がよいでしょう。
DoCmd.OutputTo メソッドは、書式を視覚的に維持した形式で出力するため、出力されたテキストファイルは罫線記号によって整形された独自の形式になります。
構造化されたデータとしての再利用はほぼできなくなりますので、出力後のファイルを Excel 等の他アプリで読み込んで再使用する可能性がある場合は、後述する DoCmd.TransferText メソッドを使用した方がよいでしょう。
ただし、DoCmd.OutputTo メソッドには引数を省略するとプロンプトが上がって、たとえば出力先のパスやファイル名のユーザーによる任意指定が可能になるという利点があります(DoCmd.TransferText メソッドで引数を省略するとエラーになります)。用途によっては、かなり便利なメソッドです。
オブジェクトをインポート/リンク/エクスポートします。
DoCmd.TransferText acImportDelim, "インポート定義1", _
"tblImported", "C:\Temp.csv", True
第 4 引数の出力ファイル名はフルパスで記述します。何か特殊な理由が無い限り、マッピングされたドライブ名(例 >> Q:\SheredFolder\Temp.txt)ではなく、UNC パス(例 >> \\ServerName\SheredFolder\Temp.txt)を使用した方がよいでしょう。
ポイントになるのは、第 2 引数に指定するインポート/エクスポート定義名です。
DoCmd.TransferText メソッドでエラーになったり、意図どおりの処理ができないケースの多くが、この第 2 引数を省略することに起因しています。
第 2 引数は、固定長テキストファイル以外の場合は省略できることになっていますが、TransferText メソッドの動作に精通しているのでない限り、インポート/エクスポート定義名は極力指定することをお奨めします。
インポート/エクスポート定義は、前もって作成しておく必要があります。
作成方法が分からないという方は、下記の手順にのっとって作成してください。
外部テキストファイルのデータを取得/追加/作成します。
これは Jet Database Engine の機能で、SQL 文中から Text-ISAM 経由で実行されます。したがって、VBA から動的クエリーとして実行させる場合は Execute メソッドあるいは DoCmd.RunSQL メソッドを、静的クエリーとして実行させる場合はあらかじめクエリーを作成しておいて、DoCmd.OpenQuery メソッドを使用することになります。
また、選択クエリーとして、OpenRecordset のソースにも使用できます。
SQL 文の記述には、IN 句を使用する 2 パターンと、使用しない 1 パターンの合計 3 パターンの記述法があり、どれを使っても結果は同じです。以下に、選択クエリーの SQL ビューに記述する前提で、3 パターン提示します。
SELECT *
FROM [Temp#CSV]
IN "" "Text;DATABASE=C:\FolderName;HDR=NO;";
SELECT *
FROM [Temp#CSV]
IN "C:\FolderName" "Text;HDR=NO;";
SELECT *
FROM [Text;DATABASE=C:\FolderName;HDR=NO;].[Temp.CSV];
上記例は、フィールド見出し行を含まない CSV 形式ファイル C:\FolderName\Temp.csv を表示する選択クエリーの例です。
これは解説が必要でしょう。
テキストファイルの場合、親フォルダをデータベースに見立て、ファイルをテーブルとして取り扱います。その際、IN 句では拡張子の前のドット( .)をシャープナンバー記号(#)に置き換えて指定します。
※「♯」(シャープ)は音楽記号で、「#」(ナンバー、ハッシュ、パウンド、いげた)が番号記号でした。
先頭行をフィールド見出し行としない場合は、HDR パラメータに "NO" を指定します。これにより、先頭から既定のフィールド名として F1, F2..., Fn が割り当てられます。
ただし、この構文はヘルプ通りではありますが、少々古いのかもしれません。
Access 2000 のクエリーに上記例のような SQL 文を記述すると、別にエラーにはなりませんが、保存時に下記のような構文に自動修正されてしまいます。
SELECT *
FROM [Temp#CSV]
IN '' [Text;DATABASE=C:\FolderName;HDR=NO;];
SELECT *
FROM [Temp#CSV]
IN 'C:\FolderName'[Text;HDR=NO;];
SELECT *
FROM [Text;DATABASE=C:\FolderName;HDR=NO;].Temp.CSV;
微妙な差ですが...。
ダブルクォーテーションが無くなっているところを見ると、クエリーの SQL ビューをコピーしてそのまま VBA のコード内に貼りつけての使用を考慮した修正ということでしょうか?
別に前出の構文も違反ではないので、どちらでも動作には支障ありません。
さて、Text-IISAM を使用して実行できる SQL 文には制限があります。
以下に表にしてみました。
ステートメント | 操作の許可 | |
---|---|---|
SELECT | 〇 | |
DELETE | × | |
UPDATE | × | |
INSERT INTO | 〇 | |
SELECT...INTO | 〇 |
これはあくまで外部テキストファイルを操作対象とした場合の話です。
たとえば更新クエリーでも、外部テキストファイルを更新することはできませんが、外部テキストファイルのデータでローカルテーブルを更新する更新クエリーであれば、ふつうに実行できます。
この辺りは理屈を並べるよりも実例を見たほうが早いので、幾つか例を挙げます。
選択クエリーに関しては前出の例を参照してください。
まず、外部 CSV ファイル C:\FolderName\Temp.csv のレコードを、ローカルテーブル tblCustomer に追加する SQL 文です。
INSERT INTO tblCustomer ( CstmrCode, CstmrName, CstmrAddress )
SELECT [Temp#CSV].F1, [Temp#CSV].F2, [Temp#CSV].F3
FROM Temp#CSV IN "C:\FolderName" "Text;HDR=NO;";
先頭行を見出し行として使用しないために、HDR=NO を指定しています。また、フィールド指定の際は自動的に割り当てられるフィールド名 F1、F2、F3 を使用し、ファイル名に含まれる # を日付リテラルの接頭辞/接尾辞と混同されないよう、[ ] で括って識別子であることを明示しています。
次は外部 CSV ファイル C:\FolderName\Temp.csv のレコードを、新規ローカルテーブル tblTempNew として新たに作成する SQL 文です。
SELECT [Temp#csv].*
INTO [tblTempNew]
FROM [Temp#csv] IN "C:\FolderName" "Text;HDR=NO;";
これで作成されるテーブルには、インデックスの設定がありません。注意してください。
最後に、ローカルテーブルを外部 CSV ファイル C:\FolderName\Exported.csv として出力する SQL 文を、気分で 2 パターンどうぞ。
SELECT tblCustomer.*
INTO [Exported#csv] IN "C:\FolderName" "Text;HDR=NO;"
FROM tblCustomer;
SELECT CstmCode, CstmName, CstmAddress
INTO [Text;DATABASE=C:\FolderName;HDR=NO;].[Exported.csv]
FROM tblCustomer;
この方法で外部 CSV ファイルにエクスポートすると、同じフォルダに Schema.ini が作成されます。中には定義情報が記録されており、次回同名のファイルをエクスポートする際には、この定義情報が優先されて使用されます。
具体的に言えば、たとえば tblCustomer を最初にエクスポートした際に、HDR=NO で見出し行を付けずに出力したとします。この時点で Schema.ini が作成されます。
2 回目に、今度は HDR=YES で出力したとします。しかし既存の Schema.ini の定義が優先されるため、出力された CSV ファイルには、見出し行が付きません。
もっとも、MS-Access にはテキストインポート/エクスポート用の DoCmd.TransferText メソッドが用意されているので、このような方法でテキスト出力を行って Schema.ini を実際に目にする機会というのは、あまり多くは無いでしょう。
ただし、固定長テキストファイルの入出力に関しては、あらかじめインポート/エクスポート定義を作成しておいて DSN パラメータで指定するか、あるいは Schema.ini を用意して Text-ISAM 情報を定義しておく必要があります。
フィールド構成が動的に変化するようなクエリーを臨機応変に固定長テキストファイルとして出力したい場合などは、あらかじめインポート/エクスポート定義を作成しておくのは困難でしょうから、Schema.ini が重宝します。
Schema.ini の詳細については、ヘルプあるいは後述する参考情報を当たってください。
また "Text;" 以下に記述される接続文字列の設定項目の実際を確認したい場合は、目的のテキストファイルのリンクテーブルをいったん手動で作成し、そのリンクテーブルをデザインビューで開いてプロパティを表示させると、説明欄にパラメータが埋め込まれていることが確認できると思います。それをコピーすれば、確実でしょう。
余談になりますが、削除できないのを承知で削除クエリーを作成して実行すると、確認ダイアログが上がりますが、そのダイアログに表示されるレコード件数が間違っている場合があります。
Access 97 においてテキストファイルからリンクテーブルを作成し、そのリンクテ−ブルを対象にした Domain 系定義域集計関数(DLookup 関数など)を使用すると、リンク直後は正常動作しますが、2 回目の起動時以後では実行時エラーになります(マクロ中で使用したりすると、無応答になります)。
これを回避するには、IN 句を使用して外部ファイルを直接参照する選択クエリーを作成し、そのクエリーをリンクテーブル代わりにするしかありません。
Domain 系定義域集計関数と同等のユーザー定義関数を作成すると問題ないので、どうやら Domain 系定義域集計関数は、機能から推測されるような単純な SQL 文を実行しているだけではなく、他にも何かやっているようです。
DAO.Recordset オブジェクトを取得します。
これにより、レコードごとの個別判定を含む複雑なデータ処理を行うことが可能になります。ただし、可能な操作は前章の許可一覧に準じます。レコードの削除や更新はできません。
現実問題として、テキストファイルをテキストファイルのまま接続して Recordset に使うことは少ないと思われます。信頼性の面からも操作性の面からも、いったんインポートしてローカルテーブルにしてからの方が、はるかに扱いやすいでしょう。
ただ、技術的にはリンクテーブルあるいは前章のような SQL 文をそのままソースにするのも、可能ではあります。
Dim strSQL As String Dim rs As DAO.Recordset strSQL = "SELECT * FROM [Temp#CSV]" _ & " IN '' 'Text;DATABASE=C:\FolderName;HDR=NO;';" Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot)
ADODB.Recordset オブジェクトを取得します。
これにより、レコードごとの個別判定を含む複雑なデータ処理を行うことが可能になります。ただし、可能な操作は前々章の許可一覧に準じます。レコードの削除や更新はできません。
DAO.Recordset と同様、現実問題として、テキストファイルをテキストファイルのまま接続して Recordset に使うことは少ないと思われます。
Dim strSQL As String Dim rst As New ADODB.Recordset strSQL = "SELECT * FROM " & _ "[Text;DATABASE=C:\FolderName;HDR=NO;].[Temp.CSV];" rst.Open strSQL, CurrentProject.Connection
上記は読み取り専用になります。レコードを追加したい場合は、Open メソッドの第 4 引数 LockType(上では省略)に適切なロックタイプを設定する必要が有ります。
ここで、Open メソッドの第 2 引数 ActiveConnection に注目してください。
SQL 文の中にパスを書き込むのは Jet プロバイダ独特の指定方法であるため、ここには Jet プロバイダの Connection を設定する必要が有ります。
MS-Access から使用する場合は、上記のように CurrentProject.Connection を指定すれば通常は問題ありません。
何らかの理由で Jet プロバイダではなく ODBC プロバイダを使用する場合は、SQL 文中にパスを書き込めません。
この場合は、以下のように接続文字列パラメータで親フォルダのパスを指定しておく必要が有ります。
Dim strSQL As String Dim strCnn As String Dim rst As New ADODB.Recordset strSQL = "SELECT * FROM Temp#CSV;" strCnn = "Provider=MSDASQL;" & _ "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _ "DefaultDir=C:\FolderName;" rst.Open strSQL, strCnn
SQL 文中のテーブル名は <ファイル名>#<拡張子> の形式で指定します。
パラメータは「DefaultDir」ではなく「DBQ」でも有効です。
また、MSDASQL プロバイダは ADO の既定プロバイダで省略可能なため、接続文字列は下記のように簡略化できます。
strCnn = "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _
"DefaultDir=C:\FolderName;"
なお前者の Jet プロバイダ版はヘッダーの有無を指定できましたが、後者の ODBC プロバイダ版ではヘッダーの指定方法が確認できませんでした。既定ではヘッダー有りとして取り扱われるようです。
いよいよ VBA の本領発揮です。
Open, Line Input #, Print # はいずれも VBA のステートメントです。
実際には関連ステートメント、関数として、これ以外にも Close、Input #、Input、Input$、Write、Put、Get などがあります。
これらを駆使すれば、テキストファイルに対してできない操作は何もありません。
裏を返せば、MS-Access あるいは DAO、Jet が用意してくれている組み込み機能に頼るわけでは有りませんので、どこまでできるかは腕次第、というシビアな領域でもあります(^ ^;)。
典型的なコード例を以下に提示します。
MS-Access 付属のサンプルデータベース NorthWind.mdb 内の運送会社テーブルをいったん CSV 形式で出力したものが存在するという前提で、それを行単位で読み込むものとします。
' CSV ファイルを 1 行ずつ読み込んで、イミディエイト ' ウィンドウに書き出します。 Const CSV_NAME As String = "C:\Temp\運送会社.csv" Dim intFileNum As Integer Dim strBuff As String intFileNum = FreeFile ' 未使用のファイル番号を取得します。 ' シーケンシャル入力モードでファイルを開きます。 Open CSV_NAME For Input Access Read As #intFileNum ' ファイルの終端までループを繰り返します。 Do Until EOF(intFileNum) ' 行を変数に読み込みます。 Line Input #intFileNum, strBuff ' イミディエイト ウィンドウに表示します。 Debug.Print strBuff Loop Close #intFileNum ' ファイルを閉じます。 ↓実行結果 1,"アカネコ","(03) 3955-98xx" 2,"トマト","(03) 3681-31xx" 3,"ペンギン","(03) 3566-99xx"
ヘルプの使用例そのまんまですな(^ ^;)。
実行結果で分かる通り、これはファイルをテキストエディタで開いたときに見える行をそのまま読み込んだ形になります。
カンマやテキスト区切り文字のダブルクォーテーションがそのままの形で読み込まれます。これをフィールド単位で区別して読み込みたい場合は、Line Input # ではなく、次の例のように Input # を使います。
' CSV ファイルをフィールドごとの変数に読み込んで、イミディエイト ' ウィンドウに書き出します。 Const CSV_NAME As String = "C:\Temp\運送会社.csv" Dim intFileNum As Integer Dim lngNum As Long Dim strName As String, strPhone As String intFileNum = FreeFile ' 未使用のファイル番号を取得します。 ' シーケンシャル入力モードでファイルを開きます。 Open CSV_NAME For Input Access Read As #intFileNum ' ファイルの終端までループを繰り返します。 Do Until EOF(intFileNum) ' 行を変数に読み込みます。 Input #intFileNum, lngNum, strName, strPhone ' イミディエイト ウィンドウに表示します。 Debug.Print lngNum, strName, strPhone Loop Close #intFileNum ' ファイルを閉じます。 ↓実行結果 1 アカネコ (03) 3955-98xx 2 トマト (03) 3681-31xx 3 ペンギン (03) 3566-99xx
CSV のために用意されたようなステートメントですね。
しかし、これだけ見ている分には、わざわざ Open しなくても、DoCmd.TransferText メソッドを使えば済むような気がするかもしれません。
実際その通りで、ほとんどの操作はローカルテーブルに取り込んで必要な処理を施してから出力すれば、済んでしまいます。
しかし、たとえば既存の HTML ファイルの途中のセクションに、データを個別に加工して出力したい場合など、細かいカスタマイズが要求される局面では必須のテクニックになってきます。
シーケンシャル アクセスについて、もう少し見ていきましょう。
ここで取り込み元として想定している CSV は、次のような内容でした。
1,"アカネコ","(03) 3955-98xx" 2,"トマト","(03) 3681-31xx" 3,"ペンギン","(03) 3566-99xx"
テキスト区切り記号として、ダブルクォーテーション(")が使用されています。
仮にテキスト区切り記号が無くても、先ほどの Input # を使用したコードで問題なく処理できます。
1,アカネコ,(03) 3955-98xx 2,トマト,(03) 3681-31xx 3,ペンギン,(03) 3566-99xx
Input # ステートメントはダブルクォーテーション(")を無視するため、有っても無くても結果に差は有りません。
では、データ自体が無い場合はどうでしょう。
1,アカネコ,(03) 3955-98xx
2,トマト, ' ← 3 列目のデータが無い
3,ペンギン,(03) 3566-99xx
これも、問題なく読み込まれます。
欠落データの読み込み用変数が文字列型であれば、空文字列(vbNullString)に、Variant 型であれば Empty 値(vbEmpty)になります。
ではでは、データはむろんのこと、カラム区切り記号のカンマ(,)までが無い場合はどうでしょう。
1,"アカネコ","(03) 3955-98xx"
2,"トマト" ' ← 3 列目のカラム区切り記号が無い
3,"ペンギン","(03) 3566-99xx"
この場合はイミディエイト ウィンドウに下記の出力がされた時点で、実行時エラーが発生します。
1 アカネコ (03) 3955-98xx 2 トマト 3
実行時エラー '62':
ファイルにこれ以上データがありません。
本来 空であるべき 2 行目 3 列目の出力位置に、3 行目 1 列目のデータがずれ込んできています。そのため、以降のデータが順次一つずつずれてしまい、最終データにいたって読み込むべきデータが見つからないというエラーになっています。
Input # ステートメントはデータの区切りをカンマ記号によって認識するため、カンマ記号自体が無いと、データの区切りを正しく認識することができなくなります。
そんな CSV ファイルは使わなければ一番いいのですが、Excel が CSV 形式でファイルを保存する際に最大列数を 16 行単位で認識する仕様になっているため、困ったことに、行の途中から右端のデータがカンマ記号ごと消え失せる CSV ファイルが、実際には世の中に氾濫しています。
このような CSV ファイルは、Input # ステートメントで処理することは出来ません。
一番簡単なのは、DoCmd.TransferText メソッドを使ってリンクなりインポートなりをしてしまう方法でしょう。
DoCmd.TransferText メソッドは、右端のデータがカンマ記号ごと消え失せている CSV ファイルも、正常に処理してくれます。
Open ステートメントにこだわって処理をするのであれば、Input # ステートメントではなく Line Input # ステートメントで行単位に読み込み、Split 関数で分割して処理をする、というロジックにするとよいでしょう。
Open ステートメントの入出力モードには、上記で使用したシーケンシャル アクセス モード以外にも、ランダム アクセス モードやバイナリ アクセス モードがあります。
ランダム アクセス モードは主に固定長データの入出力に使用されます。
実例を挙げてみます。
たとえば、NorthWind.mdb の運送会社テーブルを、次のような設定で固定長のテキストファイルに出力したとしましょう。
このファイルを読み込む場合は、ユーザー定義のデータ型を宣言してから、Get ステートメントを使います。
Private Type 運送会社 ' ユーザー定義型を定義します。 運送コード As String * 8 運送会社 As String * 20 電話番号 As String * 12 改行 As String * 2 End Type Public Sub GetFixedLenText() ' 固定長テキストファイルをユーザー定義型変数に読み込んで、 ' イミディエイト ウィンドウに書き出します。 Const TXT_NAME As String = "C:\Temp\運送会社.txt" Dim intFileNum As Integer Dim my運送会社 As 運送会社 ' 未使用のファイル番号を取得します。 intFileNum = FreeFile ' シーケンシャル入力モードでファイルを開きます。 Open TXT_NAME For Random Access Read As #intFileNum _ Len = Len(my運送会社) ' ファイルの終端までループを繰り返します。 Do Until EOF(intFileNum) ' レコードを読み込みます。 Get #intFileNum, , my運送会社 Debug.Print my運送会社.運送コード, _ my運送会社.運送会社, _ my運送会社.電話番号 Loop Close #intFileNum ' ファイルを閉じます。 End Sub ↓実行結果 1 アカネコ (03) 3955-98xx 2 トマト (03) 3681-31xx 3 ペンギン (03) 3566-99xx
ここでポイントになるのは、ユーザー定義型に改行コードの領域も含めておくことでしょう。
これが無いと、1 レコード読み込むごとに 2 バイトずつずれていってしまいます。
もしホストから出力したようなファイルで、レコード間に改行コードが含まれない形式であれば、この改行用のメンバは不要です。
また Windows 系 OS では改行コードは 2 バイトですが、Unix や Macintosh では 1 バイトになるので、他の処理系から出力されたテキストを読み込む場合は改行用のメンバのサイズが変わってきます。
ただ、プログラミングというのは何の処理でもそうですが、固定長だからといって必ずしもランダム アクセスで読まないとダメというものではありません。
別に Line Input や Input 関数で行ごと読んでから Mid 関数で切り出しても、DoCmd.TransferText メソッドでいったんテーブルに取り込んでも、同じ結果を手にすることは出来ます。
もうこの辺りは、好みの世界ですね。
ただ、いろいろ知っていると選択肢の幅が広がりますし、いわゆる適材適所というのも、どんな「材」達が有るのか知らないと使い分けようが無いので、知識として入れておくのは決して無駄ではないでしょう。
さて、最後に挙げたバイナリ アクセス モードですが、これはバイナリ ファイルの入出力はもちろん、通常のテキストファイルを高速に読み書きする場合にも使われます。
この辺りは初心者がもっとも混乱しがちなポイントでもあり、また逆に言えばもっとも応用し甲斐のある処理なのですが、いかんせんまともに解説しようとすると紙幅がいくらあっても足りません。
悲しいことに Office VBA のヘルプはこの点に関して充実しているとはとても言えず、詳しくはヘルプを参照のこと、と言って片付けるにしてはあまりにも貧弱な情報しか収録されていません。
私は本家 VB ではなく Office VBA から入った人間なので、はっきり言ってこの点に関してはかなり苦労させられました。
いずれ単独で取り上げる機会を設けるかもしれませんが、ひとまず有益と思われるリンクを一通り紹介しておきます。
ファイル入出力に関する Microsoft 社の資料です。
上記の中でもっとも詳細な解説が加えられているのは 6. なのですが、残念ながら邦訳が見当たらないようです(よくある話ですが)。
続いて、Binary モードでテキストファイルを高速に読み書きするサンプルです。これはよく知られたテクニックのようですが、オフィシャルな解説は無いようです。それでも情報が得られる辺り、インターネット時代のありがたさですね。
最後に、Open ステートメントでいかにテキスト出力を自在に操れるかを示す実例として、HTML ファイルへの出力例を挙げます。このトピックは、ファイル I/O のサンプルとして以外の意味でもいろいろ勉強になる、実に強力なトピックです。
AccessデータベースのWebコンテンツ化
FileSystemObject は Access 2000 (VBA) から導入されたオブジェクトで、ファイルの管理を行います。
このFileSystemObject オブジェクトの OpenTextFile メソッドや OpenAsTextStream メソッド、CreateTextFile メソッドを使用すると、テキストファイルに対してシーケンシャルアクセスを行う TextStream オブジェクトを作成することが出来ます。
TextStream オブジェクトは、バイナリアクセスとランダムアクセスをサポートしません。ファイル汎用のアクセス手段ではなく、テキストファイルの扱いに特化したオブジェクトです。
※ ただし MS のアナウンスによると、将来的にはバイナリアクセスをサポートするための拡張が予定されているようです。
この予定は立ち消えになるかもしれません。VBScript は対 JavaScript 戦争において敗退したため、事実上メンテナンスモードに突入しています。そのため VBScript 支援ライブラリの性格が濃かった Scripting Runtime Library をこれ以上充実させる意味が MS 的にはすでに失せています。おそらくアナウンスも特に無いまま放置プレイの公算大ではないでしょうか。VBScript が気に入っている YU-TANG 的には、ある日バイナリアクセスがサポートされて、YU-TANG の読み違いであったことが証明される日が来て欲しいと思うのですが。(YU-TANG@2003/8/15)
元々が Windows Scripting Host 用のオブジェクトだったため、VBA ライブラリの Open ステートメントなどと機能的には重複しています。さすがにそこまで融通は利きませんが、逆にテキストファイルの読み書きで主に使用されるメソッドを統合してあるため、コーディングがかなりすっきりと収まります。
テキストファイルに対する主要な操作のほとんどは、これで賄えるでしょう。
以下は使用例です。
' テキストファイルを一気に読み込み、イミディエイト ' ウィンドウに表示します。 Dim fs As Object, ts As Object Set fs = CreateObject("Scripting.FileSystemObject") Set ts = fs.OpenTextFile("C:\Temp.txt") ' テキストファイルが空でなければ読み込みます。 If ts.AtEndOfStream = False Then Debug.Print ts.ReadAll ts.Close: Set ts = Nothing Set fs = Nothing
前出の Open ステートメントの使用例と比較すると、そのスマートさが際立ちますね。
Access 97 以前でも、Microsoft Scripting Runtime に参照設定することによって、FileSystemObject オブジェクトを使用することができます。また、Access 2000 以降でも、FileSystemObject オブジェクト用の定数を使いたい場合やアーリーバインディングを行う場合には、やはり Microsoft Scripting Runtime への参照設定が必要になります。
Win32 API は、本来 Access VBA の範疇ではありません。また私自身、別に API に詳しいわけでもありません。したがって、ここで詳しい説明はしませんし、また出来る知識も持ちません。
では、なぜここであえて取り上げるのかというと、ちょっとショッキングなほど処理速度に差があることが判明したからです。
ここでは、私がそれを知ったサイトを勝手にリンクするに留めます。
そちらをご覧になれば、後は説明不要でしょう。
猫の知ったかぶり > ファイル・フォルダ関連 > ファイルの読み込み方法各種
※ 2005/01/28 時点ではサイトが閉鎖されていましたが、2006/7/7 に七夕プレゼントとして(?)オフライン版が公開されています(Thanks for notification, nil)。
VB5 当時の内容ですが、今なお代えの効かない極上ネタがひしめいていますので、ぜひこの機会にダウンロードを!
関連主要 API について調べる場合は、ReadFile、ReadFileEx、WriteFile、WriteFileEx をキーワードに検索するとよいでしょう。
ReadFile/WriteFile API については、日経ソフトウェアの連載「APIから知るWindowsの仕組み」第 5 回(2002/11 月号)と第 19 回(2004/1 月号)で詳しく解説されています。
ソースコードは下記からダウンロードできます(C ですが ^^;)。
また、以下の VB 用 MSKB も参考になります。