TL;DR — 組み込みの VBA でテキストファイルを読み書きするには、番号付きのチャネルを開きます:
Open path For Output As #n。番号nは、あとに続くすべてのPrint #nやClose #nが参照する ハンドルです — それはFreeFileから受け取り、#1を決してハードコードしてはいけません。#1を ハードコードした 2 つのファイルはerror 55を発生させ、For Outputは既存のファイルを空に 切り詰め、一度もCloseしないチャネルは Excel を終了するまでロックされたままです。
Sub WriteReport()
Dim n As Integer
n = FreeFile ' 次に使える空きチャネル番号を取得する
Open "C:\Reports\out.txt" For Output As #n
Print #n, "Hello" ' すべての書き込みはその番号を参照する
Close #n ' つねにチャネルを解放する
End Sub
VBA でテキストファイルにたどり着く道は、テキストからではなく番号から始まります。Open
ステートメントはファイルをチャネルに結びつけ、その時点から、あらゆる書き込み・読み込み・クローズは、
パスではなくその番号でチャネルを名指しします。FreeFile は、安全に使える番号をあなたに渡すために
存在します。この一点さえ正しくつかめば、組み込みのファイル API はもう扱いにくいものではなくなります。
逆に間違えれば — 番号を自分で選べば — 2 つ目のファイルを開いた瞬間にだけ姿を現すバグを書いたことに
なります。
この記事で学べること
- 考え方の軸 —
FreeFileはチャネル番号を渡す。変数で受け取る #1をハードコードすると、2 つのファイルを開いた瞬間にerror 55になる理由- 3 つのモード —
For Output(切り詰め)、For Append(追加)、For Input(読み込み) — と、どれが無言でデータを消すか OpenステートメントがWorkbooks.Openではない理由- 一度も
Closeしないチャネルが、Excel を終了するまでファイルをロックする理由 - チャネルをつねに解放する
On Errorクリーンアップのパターン
考え方の軸:FreeFile はチャネル番号を渡す
鍵となる洞察は、組み込みのファイル I/O がチャネルを通して働き、そのチャネルが小さな整数で
識別される、ということです。Open ... As #n はファイルを番号 n に結びつけ、その後は Print #n、
Input #n、Close #n がすべて番号でそのファイルを相手にします。パスが現れるのはちょうど一度
だけ — Open の中でです。
FreeFile は、現在使われていない次のチャネル番号を返します。人がつまずくルールはこれです。開く前に
格納しておくこと。 その番号でファイルが実際に開かれるまで、FreeFile は同じ値を返し続けるので、
一度だけ受け取って変数を使い回さなければなりません。
Dim n As Integer
n = FreeFile ' 一度だけ受け取る
Open "C:\data.txt" For Output As #n
' ... #n を使う ...
Close #n
FreeFile を「空きチャネルをちょうだい」、n をその整理券だと考えてください。同じ整理券番号を
2 人の客に貼り付けることは決してしないはずです。FreeFile は、ファイルで同じことをしないための
仕組みなのです。
#1 をハードコードすると error 55 になる理由
日常の近道 — Open path For Output As #1 — は、デモでは完璧に動きます。開くファイルが 1 つしか
ないからです。それがバグに変わるのは、#1 がまだ開いているうちに、あるルーチンが 2 つ目のファイルを
開いた(あるいはそうする別のルーチンを呼んだ)瞬間です。
Open "a.txt" For Output As #1
Open "b.txt" For Output As #1 ' error 55 — "File already open"
error 55 は「そのチャネル番号はすでに使われている」という意味です。入れ子のマクロ、メインルーチンが
自分のファイルを開いている最中に書き込むロガー、クローズを忘れるループ — どれもハードコードした
番号の上で衝突します。FreeFile はこのバグの一群をまるごと取り除きます。すでに使われている番号を
決して手渡さないからです。
Dim nA As Integer, nB As Integer
nA = FreeFile: Open "a.txt" For Output As #nA
nB = FreeFile: Open "b.txt" For Output As #nB ' 別の、空いている番号
リテラルの #1 をタイプした瞬間、あなたはチャネル番号を手で決めてしまったのです — それはまさに、
FreeFile があなたの代わりに下すために作られた決定です。
3 つのモード:Output は切り詰め、Append は追加、Input は読み込み
Open ... For <mode> は、シーケンシャルなテキストに対して 3 つのモードのいずれかを取ります。そして
間違ったものを選ぶことは、データを失ういちばんありふれた方法です。
For Output— ファイルがなければ作り、すでに存在すれば中身を空に切り詰めます。既存の ファイルをFor Outputで開くと、一行も書かないうちにその中身を消します。これが、あの有名な 「ログファイルが何度も消える」バグです。For Append— ファイルがなければ作り、そうでなければ末尾に追加します。増え続けるログに ほしいのはこれです。For Input— 既存のファイルを読み込み用に開きます。ファイルがなければerror 53 File not foundを発生させるので、先にDirでガードしましょう。
Open logPath For Append As #n ' 履歴を残す
Open logPath For Output As #n ' 同じファイル — 先に履歴を消す
2 つの書き込みモードは、見た目はほとんど同じなのに、既存のファイルに対して正反対のことをします。
ファイルが時とともに増えていくべきなら、つねに For Append です。For Output は、毎回ゼロから
書き直すつもりのファイルのためのものです。
Open ステートメントは Workbooks.Open ではない
Open は、テキストファイルをチャネル番号に結びつけるステートメントです。スプレッドシートを
読み込むこととは何の関係もありません。.xlsx を Open ... For Input で開いても、得られるのは zip
コンテナの生のバイト列であって、セルを読めるブックではありません。
Open "Book.xlsx" For Input As #n ' 生のバイト列 — 使えるブックではない
実際のブックを読み込むには、メソッドの Workbooks.Open を使います。
これはシートとセルを持つ Workbook オブジェクトを返します。この名前の衝突は、よくある混乱のもと
です。Open(ステートメント)はテキストチャネルのため、Workbooks.Open(メソッド)は
ブックのためのものです。ファイルが行と列としてほしい .csv なら、自分でテキストを解析するより、
ブックとして開くほうがたいてい簡単です。
閉じていないチャネルがファイルをロックする理由
すべての Open は Close と対にしなければなりません。一度も閉じないチャネルは、ブックまたは Excel
自体が終了するまで、ファイルをロックしたままにします — 開き直すことも、
Kill で削除することも、Excel で開くこともできません。罠は、Open と
Close のあいだで起きるエラーです。エラーはあなたの Close 行を飛び越え、チャネルが漏れます。
堅牢な形は、Close をエラーハンドラーに置いて、書き込みが成功しても失敗しても実行されるように
します。
Sub SafeWrite(ByVal path As String, ByVal text As String)
Dim n As Integer
n = FreeFile
On Error GoTo Cleanup
Open path For Output As #n
Print #n, text
Cleanup:
Close #n ' 成功時にも、エラー時にも実行される
End Sub
Close #n は 1 つのチャネルを解放します。番号のない裸の Close は、開いているすべてのファイルを
一度に閉じます — 便利な最後の手段ですが、大雑把なやり方です。確実なクリーンアップの完全なパターンは
On Error を参照してください。
正直な結論:FreeFile、変数、そして保証された Close
組み込みの Open/Close API は速く、依存もありません — ただチャネルを尊重することだけを求めます。
4 つのルールで足ります。
- 番号を決してハードコードしない →
n = FreeFileを一度、そのあとはどこでも#nを使う。 リテラルの#1は潜在的なerror 55です。 - モードを知る →
For Appendはファイルを増やし、For Outputは先に消し、For Inputは ファイルが存在している必要があります。 - ステートメントはメソッドではない →
Openはテキストチャネルを開き、Workbooks.Openはブックを開きます。 - つねに Close する →
Close #nをOn Errorハンドラーに置いて、書き込み途中の失敗がファイルを ロックしたまま残せないようにする。
ほとんどのファイルバグを防ぐたった一つの習慣は、すべてのルーチンの最初の一行です。n = FreeFile。
番号を手で選べば、VBA が正しく選ぶ用意のあった唯一のものを、あなたが選んでしまったことになります。
ExcelMaster の活用
テキストファイルを安全に書くとは、#1 をハードコードする代わりにチャネル番号を FreeFile から取り、
履歴を残さなければならないときは For Output ではなく For Append を選び、書き込みが失敗しても
Close を保証することです — ただファイルを開くだけに見えるステートメントを取り巻く、3 つの細部です。
ExcelMaster は、漏れも消去も
しないファイル処理ルーチンを書きます。ジョブを説明すれば — 「各実行をブックの隣のログに追記して」、
あるいは「この範囲をテキストファイルに書き出して」 — FreeFile の受け取り、正しいモード、そして
保証された Close を、ThisWorkbook.Path に固定して生成します。あなたはほしい
ファイルを説明するだけ。チャネルを正しく開き、つねに解放するコードを書くのは、それがやります。
よくある質問
VBA で FreeFile は何をしますか?
FreeFile は、現在使われていない次のファイルチャネル番号を返すので、番号を当て推量せずにファイルを
開けます。ファイルを開く前に変数で受け取りましょう — n = FreeFile: Open path For Output As #n —
FreeFile は、その番号でファイルが実際に開かれるまで同じ値を返し続けるからです。ハードコードした
#1 の代わりに FreeFile を使えば、2 つ以上のファイルが同時に開いているときの
error 55 File already open を防げます。
VBA でテキストファイルを開くには?
Open ステートメントに、モードとチャネル番号を添えて使います:Open "C:\data.txt" For Input As #n、
ここで n = FreeFile です。作成または上書きには For Output、末尾への追加には For Append、
読み込みには For Input を使います。終わったらつねに Close #n と対にしましょう。これが開くのは
テキストチャネルであることに注意してください。スプレッドシートを読み込むには、代わりに
Workbooks.Open を使います。
VBA が error 55 File already open を出すのはなぜですか?
Open に渡したチャネル番号がすでに使われているからです — ほとんどの場合、コードが #1 を
ハードコードし、最初のものが閉じられる前に 2 つ目の Open がそれを再利用するためです。すべての
リテラル番号を、それぞれの変数に受け取った n = FreeFile に置き換え、終わったらすぐに各チャネルを
Close #n で閉じて、番号が二重予約されないようにしましょう。
VBA で For Output は既存のファイルを削除しますか?
はい。既存のファイルを For Output で開くと、何かを書く前に中身を空に切り詰めます — 古い内容は
消えます。以前の内容を残して追加したいなら、代わりにファイルを For Append で開きましょう。
For Output は、毎回ゼロから書き直すつもりのファイル専用です。
VBA でファイルを閉じないとどうなりますか?
ブックまたは Excel が終了するまで、ファイルはそのチャネル上でロックされたままになります — 開き
直すことも、Kill で削除することも、Excel で開くこともできません。エラーが
書き込みを中断したときの漏れを避けるには、Close #n を On Error クリーンアップ区間に置いて、
書き込みが成功しても失敗しても実行されるようにしましょう。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-25。
関連ガイド: VBA Print # vs Write # · VBA Read Text File · VBA Open Workbook · VBA FileSystemObject · VBA On Error
