テキストファイルの読み書き(TextStream編)
VBAを使い「テキストファイルを読み書き」する手法について、以下のようなシリーズで紹介しています。
①テキストの文字コードについて
②Openステートメント法による読み書き
③ADODB.Stream法による読み書き
④TextStream法による読み書き ←今回
⑤XMLHTTP法によるWebデータの読み取り (作成中)
⑥Web上からダウンロードしての読み取り (作成中)
TextStreamは、FileSystemObjectオブジェクトから作成されるオブジェクトです。このTextStreamを使用することで、テキストを操作することが出来ますので、今回紹介します。
「TextStream法」は、下表の如く複数の文字コードに対応できる手法です。なお操作可能なのは、LAN上(ドライブ名やサーバー名で始まるパス名の場所)のファイルのみです。
読み書き手法 | 対応文字コード | ファイルの場所 | ||||
---|---|---|---|---|---|---|
Shift-JIS | UTF-8 | UTF-16 | LAN上 | Web上 | ||
② | Openステートメント | 〇 | △ | 〇 | ||
③ | ADODB.Stream | 〇 | 〇 | 〇 | 〇 | |
④ | TextStream | 〇 | 〇 | 〇 | ||
⑤ | XMLHTTP | 〇 | 〇 | 〇 | 〇 |
手法全体の話ですが、図01のように手法ごとに「対応できる文字コードが異なる」原因の1つとして、手法の生まれた時期と関係がありそうです。下図は各手法の歴史です。今回紹介するFileSystemObjectは1998年から存在したことが分かります。
図02
同様に、各文字コードの歴史をまとめたのが下図です。FileSystemObjectが生まれた頃には、Shift-JISはもちろんUnicodeも既に存在しているのですが、Unicodeの全てに対応出来ている訳ではありません。
勝手な推測ですが、Unicodeの将来性を少し見誤ったのかもしれません(と言って、EUC等に対応できている訳でも無い)。
図03
1.FileSystemObjectオブジェクト
FileSystemObjectオブジェクトは、ドライブやフォルダ、ファイルを操作するオブジェクトです。操作のためのメソッドやプロパティは数多く存在し、人間がエクスプローラ等を使って操作する代わりにプログラムで実行するイメージです。今回のTextStreamは、その中の「ファイル内のテキストを読み書き」するオブジェクトですが、そのTextStreamオブジェクトを作るためには、その上流であるFileSystemObjectオブジェクトが必要です。
1ー1.FileSystemObjectオブジェクト生成
FileSystemObjectオブジェクトをVBAで使うには、まずオブジェクトを生成する必要があります。その生成方法には以下の2種類があります。「事前バインディング」・・・コードを実行する前に生成
「実行時バインディング」・・コードを実行した時に初めて生成
事前バインディングの特徴は、コード作成時にインテリセンス(コード補完機能=使用可能なプロパティ等が表示され、選択することが可)が使える事と併せ、効率が良い(≒実行速度が速い)ことです。
一方実行時バインディングは、実行しているPCに合わせたライブラリを呼び出すため、PC環境の異なる複数ユーザーにマクロを配布する時でも安全度は高いと思われます。
1ー1ー1.事前バインディング
事前バインディングは、VBE(コードを書くウィンドウ)上部の「ツール」→「参照設定」から、図04の右側のように「Microsoft Scripting Runtime」にチェックをし有効にします。図04
「ライブラリーを参照設定」した上で、プログラム的には図05のように「オブジェクト変数をFileSystemObjectオブジェクトとして宣言」→「New句を使って生成」します。その生成したFileSystemObjectオブジェクトを使って、TextStreamオブジェクトを作成します。
- '========== ⇩(1) 事前バインディングでの宣言と生成 ============
- Dim Fso As FileSystemObject '←FileSystemObjectオブジェクト型変数の宣言
- Dim Fst As TextStream '←TextStreamオブジェクト型変数の宣言
- Const FN As String = "Test_SJIS.txt" '←開くファイル名
- Set Fso = New FileSystemObject '←FileSystemObjectオブジェクトの生成
- Set Fst = Fso.OpenTextFile(FN) '←TextStreamオブジェクトの作成
- (作成したTextSteamオブジェクト変数(Fst)を使い、テキスト処理)
01行目「Dim Fso As FileSystemObject」で、FileSystemObjectオブジェクト型で変数宣言をします。
05行目「Set Fso = New FileSystemObject」では、New句を使ってFileSystemObjectオブジェクトを生成します。
一方、02行目「Dim Fst As TextStream」では、TextStreamオブジェクト型で変数宣言をします。
06行目「Set Fst = Fso.OpenTextFile(FN)」では、05行目で生成したオブジェクト変数Fsoを使って、TextStreamオブジェクトを作成しています。
オブジェクト作成後は、そのオブジェクト変数(図05では、Fst )を使って処理作業を行います。
なお02行目を「Dim Fst As Object」と一般Object型で宣言したとしても、06行目のOpenTextFileメソッドの戻り値が「TextStreamオブジェクト」ですので、変数Fstは勝手に「TextStreamオブジェクト型」になってくれます。
1ー1ー2.実行時バインディング
実行時バインディングの場合は、CreateObject関数を使ってFileSystemObjectオブジェクトの生成をします。- '========== ⇩(2) 実行時バインディングでの宣言と生成 ============
- Dim Fso As Object '← 一般オブジェクト型変数の宣言
- Dim Fst As Object '← 一般オブジェクト型変数の宣言
- Const FN As String = "Test_SJIS.txt" '←開くファイル名
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectオブジェクトの生成
- Set Fst = Fso.OpenTextFile(FN) '←TextStreamオブジェクトの作成
- (作成したTextStreamオブジェクト変数(Fst)を使い、テキスト処理)
21行目「Dim Fso As Object」では、単なるObject型として変数宣言をします。
25行目「Set Fso = CreateObject("Scripting.FileSystemObject")」では、CreateObject関数を使ってFileSystemObjectオブジェクトを生成します。
一方22行目「Dim Fst As Object」では、単なるObject型として変数宣言をします。
26行目「Set Fst = Fso.OpenTextFile(FN)」では、25行目で生成したオブジェクト変数Fsoを使って、TextStreamオブジェクトを作成しています。
オブジェクト作成後は、そのオブジェクト変数(図06では、Fst )を使って処理作業を行います。
なお以下のサンプルコードは、全て実行時バインディングでFileSystemObjectオブジェクト生成をしています。
2.TextStreamの作成
TextStreamというと、あまり馴染みが無いと思います。このTextStreamオブジェクトはFileSystemObjectオブジェクトから作成されます。図07
そのTextStreamオブジェクトの作成方法には、図07のように3種あります。
① | ファイル名を指定してOpenTextFileメソッドを実行することでTextStreamオブジェクトを作成。 | |
② | ファイル名を指定してGetFileメソッドを実行することでFileオブジェクトを一旦作成、そのFileオブジェクトからOpenAsTextStreamメソッドを実行することでTextStreamオブジェクトを作成。 | |
③ | ファイル名を指定してCreateTextFileメソッドを実行することでTextStreamオブジェクトを作成。 |
2-1.OpenTextFileによる作成
図07の①のルートが、OpenTextFileメソッドによるTextStreamオブジェクトの作成です。構文は以下です。FileSystemObject.OpenTextFile (filename, [ iomode, [ create, [ format ]]] ) As TextStream
4つの引数の内容は以下になります。
引数 | 型 | 内容 | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | filename | 必須 | String | [パス名+] ファイル名 | ||||||||||||
2 | iomode | 省略可 | IOMode (右表) | 入出力モード
| ||||||||||||
3 | create | 省略可 | Boolean | 指定したファイルが存在しない場合の処理
| ||||||||||||
4 | format | 省略可 | Tristate (右表) | 開くファイルの文字コード
|
第1引数(filename)は、開くファイル名です。パス名を省略した場合には、既定のドキュメントフォルダー(C:¥Users¥[ユーザー名] ¥Documents")に存在するファイルが対象になります。
パス名を付ける場合は、絶対パス名(C:¥・・・等)または相対パス名が可能です。相対パスの起点は上記のドキュメントフォルダーです。
第2引数(iomode)は、ファイルを「読み取り専用/書き込み専用/追記モード」のどれで開くかを指定します。既定は読み取り専用です。
第3引数(create)は、第1引数(filename)で指定したファイルが存在しなかった時の処理です。Trueの場合は、filenameで指定したファイルを新規作成しますし、False(既定)の場合には「ファイルが見つかりません」との実行時エラーが発生します。
なお、第2引数がForReading(読み取り専用)の時に第3引数をTrueにした時にも「新規ファイルは作成」されますが、中身が無いのでTextStreamをRead等で読み込む時にはエラーが出てしまいます。
データ読み取り用にOpenTextFileメソッドを使用する時には、ファイルが実在するか否かを事前に調べる必要があります。
第4引数(format)は、開くファイルの文字コードを指定します。一見すると図08内の表は「値=0 はShift-JIS」「値=-1 はUnicode」「値=-2 は、システム既定??」と見えますが、そんなに単純では無さそうです。
まず、第4引数の各値とファイルの文字コードの対応を調べた結果を下表に示します。×印は文字化け発生です。
ファイルの文字コード | Tristate (値=0) | Tristate (値=-1) | Tristate (値=-2) | |
ANSI(Shift-JIS) | ◎ | × | ◎ | |
UTF-8 | BOM無 | × | × | × |
BOM付 | × | × | × | |
UTF-16LE | BOM無 | × | 〇 | × |
BOM付 | × | ◎ | 〇 | |
UTF-16BE | BOM無 | × | × | × |
BOM付 | × | 〇 | × |
図09
図09を見ると、第4引数(文字コード指定)の意味には2種類ありそうです。
1つ目は「読み取り専用(ForReading)」及び「追記モード(ForAppending)」の時に、ファイルからTextStreamに正しくデータを取り込める文字コードはどれなのか という意味で、上表の〇印+◎印に相当します。ファイルの文字コードに合わせて第4引数の値を指定しないと文字化けしてしまう事になります。
元々図01でも、TextStreamオブジェクトは「Shift-JISとUTF-16にしか対応できない」と説明してきましたが、その詳細が図09という事になります。
また「追記モード」の時には、TextStreamに読み込んだ文字に対して追記をする事になるのですが、その「追記する文字コード」は何か というのが問題になります。もし、以下で説明する「書き込みの文字コード(図09の◎印)」で追記されるのであれば、読み込み部分と追記部分で文字コードが異なる可能性が出てきます。しかし調べてみると、そういう現象にはなりませんでした。
ForAppendingの場合は「ファイルの文字コードでTextStreamに格納」され、且つ「読み込んだファイルの文字コードで追記」されるようです。つまり「ファイルの処理前後で文字コードは不変」ということになります。
(図09の引数formatと文字コードの対応について) まず「値=-1」はUnicode形式であり「UTF-16LE」の事ですので、UTF-16LEのBOM有・無が値=-1 で読み込めるのは当然と感じます。しかしちょっと驚いたのがUTF-16BE(BOM付)が値=-1 で読み込める事です。ファイルのBOMを頼りにしてバイト順を修正しながら読み込んでいるのかもしれません。 問題は「値=-2」です。 「システム既定」と説明されているので、これを「Windowsの内部コード」だと解釈すれば、Windows98以降はUTF-16となる(Windows XP以前はShift-JIS拡張のWindows-31J)ようです。しかし実際に値=-2で書き込んでみると、テキストは「Shift-JISで保存」されるのです。 また「値=-2」の設定では、UTF-16LE(BOM付)とShift-JISの両方が読み込めるのです。 実は値=-2 の定数には「TristateUseDefault(システム既定)」の他に「TristateMixed(混在)」という定数があるのです。 しかし、もしShift-JISとUTF-16の文字コードが混在しているファイルが存在していたとしても、ひどい文字化けになるはずなので「文字コードが混在しているファイルが読める」という意味では無さそうです。この混在というのは「Shift-JIS または Unicode」というような意味なのかもしれません。 但しLEのBOM無しは読めず、またBEのBOM付も読めないというのは、ちょっと中途半端な気がします。 以上より考えると、TextStreamオブジェクトというのは「文字をメモリ上に読み込んでバイト列として管理」しているのでは無く、実際にファイルを開いて文字を読んだり書いたりしているように見えます。ファイルが正しく開けるか否かも、アプリがその文字コードに対応しているか否かだけの話ですので「TextStreamは実際のファイルを直接操作し、文字をバイト列では無くファイル上の文字として扱う」と考えれば、色々な動作が頷ける気がします。 |
2つ目は「書き込み専用(ForWriting)」の時の文字コードです。書き込み専用の時には「TextStreamが空の状態」からスタートしますが、その状態で使われる文字コード(図09の◎印)ということになります。
しかし「よりみち」でも書いた通り、「システム既定(値=-2)」ではShift-JISとなるのはちょっと理解できません。
なおMicrosoftサイト等では、既定のTristateFalse(値=0)は「Ascii形式」と説明されていますが、Asciiの親戚関係とも言える「Shift-JIS」にも対応していると判断できたため、図08では「Shift-JIS形式で開く」としました。
(既定文字コードの変更) 上記「よりみち」でも説明したように、Windowsの「システム既定文字コード」は「UTF-16」ですが、Windows10以降では「既定文字コードをUTF-8に変更」することが可能です。 変更方法については様々なサイトで紹介されている通り、「システムロケールの変更」ダイアログの「ベータ:ワールドワイド言語サポートでUnicode UTF-8を使用」を有効化(レ点を付ける)→再起動をすれば意外と簡単に変更できます。 (但し、絶対にお勧めしません。試すのもやめた方が良いです。後悔します。) 私も試してみようとシステムをUTF-8に設定してみました。 しかし、UTF-8既定の環境では、VBE内での全角文字は「全て文字化け」となります。例えば、VBE左側のプロジェクトWindowでは「標準モジュール」という文字も化けてしまいますし、コード内の全角文字も化けてしまい「何という文字を処理しようとしているのか分からない」状態です。 加えて、UTF-8既定状態の時に開いたExcelファイル(標準モジュール付)は、システム既定を元に戻した後では「ファイル異常」と判断され、やっと開けたと思ったら「標準モジュールが削除」される事態となりました。まだベータ版の域にも達していない気がします。 また、システムをUTF-8にした状態で「TristateUseDefault(値=-2)」を選択してOpenTextFileメソッドでファイル書き込みを行った所、UTF-8(BOM無し)のファイルが作られたのです(かなり慌てていたので、操作を間違えた可能性もありそう)。 という事は「通常のシステム既定はShift-JIS ?」ということになり、上記の「システム文字コード=UTF-16」と矛盾します。 いずれにしても、VBEのコードが読めなくなるのでは困ります。FileSystemObjectでのUTF-8対応はあきらめ、ADODB.Streamオブジェクトを利用した方がよっぽど良いと思います。 |
OpenTextFileメソッドを使ったTextStreamオブジェクトの作成コード例を以下に示します。
- '========== ⇩(3) OpenTextFileによるTextStreamの作成 ============
- Sub test01()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 2, True, 0) '←TextStreamの作成
- Fst.Write "Aアあ"
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
46行目「Set Fso = CreateObject("Scripting.FileSystemObject")」では、FileSystemObjectオブジェクト(オブジェクト変数Fso)を生成しています。
47行目「Set Fst = Fso.OpenTextFile(FN, 2, True, 0)」では、46行目で作成したFileSystemObjectオブジェクトからOpenTextFileメソッドを実行してTextStreamオブジェクトを作成し、オブジェクト変数Fstとしています。
なおOpenTextFileメソッドに指定する引数は、図08に従って設定します。
49行目は処理の例で、ここではファイルに文字列を書き込んでいます。
開いたファイルは50行目「Fst.Close」で閉じ、作成したオブジェクトは52~53行目でオブジェクトへの参照を解除しています。
2-2.GetFile+OpenAsTextStreamによる作成
図07の②のルートが、GetFileメソッド+OpenAsTextStreamメソッドによるTextStreamオブジェクトの作成です。まずGetFileメソッドの実行により、指定したファイルに対応したFileオブジェクトが作成されます。Fileオブジェクトのプロパティではファイルの情報が取得できますし、メソッドを使うことでコピーや移動・削除等ができます。
そのメソッドの1つである「OpenAsTextStreamメソッド」を使用することでTextStreamオブジェクトが開き、ファイル内容へのアクセスが出来るようになります。
2-2-1.GetFileによるFileオブジェクトの作成
ファイル名を指定してGetFileメソッドを実行することでFileオブジェクトが作成されます。構文は以下です。FileSystemObject.GetFile (filepath) As File
引数は以下の1つのみで、開くファイルを指定します。
パラメータ | 型 | 内容 | ||
---|---|---|---|---|
1 | filepath | 必須 | String | [パス名+] ファイル名 |
引数(filepath)は、開くファイル名です。パス名を省略した場合には、既定のドキュメントフォルダー(C:¥Users¥[ユーザー名] ¥Documents")に存在するファイルが対象になります。
パス名を付ける場合は、絶対パス名(C:¥・・・等)または相対パス名が可能です。相対パスの起点は上記のドキュメントフォルダーです。
なお指定したファイルが無い場合は、「ファイルが見つかりません」の実行時エラーが発生します。図08のようなcreate引数はありませんので、ファイルの実在は必須です。
2-2-2.OpenAsTextStreamによるTextStreamの作成
FileオブジェクトのOpenAsTextStreamメソッドの実行で、TextStreamオブジェクトが作成されます。構文は以下です。File.OpenAsTextStream ([ iomode, [ format ]]) As TextStream
引数は以下の2つです。
パラメータ | 型 | 内容 | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | iomode | 省略可 | IOMode (右表) | 入力/出力モード
| ||||||||||||
2 | format | 省略可 | Tristate (右表) | 開くファイルの形式
|
第1引数(iomode)は、ファイルを「読み取り専用/書き込み専用/追記モード」のどれで開くかを指定します。既定は読み取り専用です。
第2引数(format)は、開くファイルの文字コードを指定します。どのformat値がどの文字コードに対応しているかは、図09を参照下さい。
GetFileメソッド+OpenAsTextStreamメソッドを使ったTextStreamオブジェクトの作成コード例を以下に示します。
- '========== ⇩(4) GetFile+OpenAsTextStreamによるTextStreamの作成 ============
- Sub test02()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fgf As Object '←Fileの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fgf = Fso.GetFile(FN) '←Fileの作成
- Set Fst = Fgf.OpenAsTextStream(1, 0) '←TextStreamの作成
- MsgBox Fst.ReadAll
- Fst.Close
- Set Fst = Nothing
- Set Fgf = Nothing
- Set Fso = Nothing
- End Sub
77行目「Set Fso = CreateObject("Scripting.FileSystemObject")」でFileSystemObjectを生成し、78行目「Set Fgf = Fso.GetFile(FN)」ではGetFileメソッドにファイル名を指定しFileオブジェクトを作成しています。
その後79行目「Set Fst = Fgf.OpenAsTextStream(1, 0)」では、OpenAsTextStreamメソッドでTextStreamオブジェクトを作成しています。
OpenAsTextStreamメソッドに指定する引数は、図12に従って設定します。
2-3.CreateTextFileによる作成
図07の③のルートが、CreateTextFileメソッドによるTextStreamオブジェクトの作成です。メソッドのスペルから分かるように「ファイル新規作成→ファイルに書き込み」というのが基本スタンスのようです。構文は以下です。FileSystemObject.CreateTextFile (filename, [ overwrite, [ unicode ]] ) As TextStream
引数は以下の3つです。
引数 | 型 | 内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
1 | filename | 必須 | String | [パス名+]ファイル名 | ||||||
2 | overwrite | 省略可 | Boolean | 既存のファイルを上書き可能か否か
| ||||||
3 | unicode | 省略可 | Boolean | 文字コード
|
第1引数(filename)は、開くファイル名です。パス名を省略した場合には、既定のドキュメントフォルダー(C:¥Users¥[ユーザー名] ¥Documents")に存在するものとなります。
パス名を付ける場合は、絶対パス名(C:¥・・・等)または相対パス名が可能です。相対パスの起点は上記のドキュメントフォルダーです。
第2引数(overwrite)は、第1引数に指定したファイルが存在した場合の処理です。Falseを指定すると「既に同名のファイルが存在しています」との実行時エラーとなります。
一方、Trueを指定(既定)すると上書きとなります。なおCreateTextFileで開くTextStreamは「書き込み専用」のため、既存のファイルの内容は全てクリアされた状態で開くので注意が必要です。
第3引数(unicode)は、書き出すファイルの文字コードの指定です。Trueを指定すると「UTF-16LE(BOM付)」、Falseを指定(既定)すると「Shift-JIS」となります。
これは、図09の「TristateFalse(値=0)」「TristateTrue(値=-1)」の列の◎印と同じという事になります。
CreateTextFileメソッドを使ったTextStreamオブジェクトの作成コード例を以下に示します。
- '========== ⇩(5) CreateTextFileによるTextStreamの作成 ============
- Sub test03()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.CreateTextFile(FN, True, False) '←TextStreamの作成
- Fst.Write "Aアあ" & vbCrLf & "123" & vbCrLf & "かきく"
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
106行目「Set Fso = CreateObject("Scripting.FileSystemObject")」でFileSystemObjectを生成し、107行目「Set Fst = Fso.CreateTextFile(FN, True, False)」で、CreateTextFileメソッドを使いTextStreamオブジェクトを作成しています。
CreateTextFileメソッドに指定する引数は、図14に従って設定します。
CreateTextFileメソッドで作成したTextStreamは「書き込み専用」ですので、109行目「Fst.Write "Aアあ" & vbCrLf & "123" & vbCrLf & "かきく"」のように書き込み用のメソッドを使用します。
(尚このコードで作ったファイルは、下のReadメソッド等での読み出し用として使用しています。)
2-4.TextStream作成法3種のまとめ
以上のようにTextStreamを作成するには3種類の方法があります。各方法の特徴を下表で整理します。TextStream 作成法 | Open | GetFile + OpenAs | Create | |
---|---|---|---|---|
ファイル名 | 既存 | 〇 | 〇 | 〇 |
新規 | 〇 | × | 〇 | |
入出力モード | 読み取り | 〇 | 〇 | × |
書き込み | 〇 | 〇 | 〇 | |
末尾に追記 | 〇 | 〇 | × | |
文字コード | システム既定 | 〇 | 〇 | × |
Unicode | 〇 | 〇 | 〇 | |
Shift-JIS | 〇 | 〇 | 〇 | |
作成されるオブジェクト | TextStream | File TextStream | TextStream |
このように比較してしまうと、一番左列のOpenTextFileメソッドでの作成が最も良いように見えてしまいます。しかし中央のGetFileでFileオブジェクトを中間作成する手法は、既にFileオブジェクトが作成済みなので、テキストの読み書きだけで無くファイル操作も同時に行う場合には役に立ちます。またファイルの新規作成→書き込みが基本作業であるならば、一番右列のCreateTextFileがコード的にも分かり易く且つ機能も必要充分です。
なお、文字コードの〇×についてはもう少し複雑ですが、詳しくは図09を参照下さい。
2-5.作成直後のTextStreamの状態とファイルポインター位置
上記の様にTextStreamを作成する手法①②では「入出力モード」の選択が可能です。その入出力モードにより、作成されたTextStreamの内容も異なりますし、またTextStreamの「ファイルポインターの位置」も異なります。
下図は、入出力モードの違いによる「TextStreamの内容」と「ファイルポインターの位置」を表しています。
図17
読み取りモード(引数のiomodeにForReading(値=1))の場合は、読み込んだファイルの内容をTextStreamに全て格納します。また、その時のファイルポインターは先頭にあります。
書き込みモード(引数のiomodeにForWriting(値=2))またCreateTextFileでTextStreamを作成した場合は、TextStreamを作成した時点で空のTextStreamになります。当然ながらファイルポインターは先頭(=文末)にあります。
追記モード(引数のiomodeにForAppending(値=8))の場合は、読み込んだファイルの内容をTextStreamに全て格納します。その時のファイルポインターは文末にあります。
3.TextStreamのプロパティ
上記の方法で作成されたTextStreamオブジェクトを使い、以下でテキストの処理を行っていきます。まずTextStreamオブジェクトのプロパティは、以下の4つです。全てのプロパティが「現在位置(=TextStreamのファイルポインターの位置)」の場所の特性を表すものです。
プロパティ | 型 | 内容 | |
---|---|---|---|
Column | 取得のみ | Long | 現在位置の列番号(1~) |
Line | 取得のみ | Long | 現在位置の行番号(1~) |
AtEndOfLine | 取得のみ | Boolean | True=現在位置は行末 False=行末では無い |
AtEndOfStream | 取得のみ | Boolean | True=現在位置は文末 False=文末では無い |
この4つのプロパティはお互いに関係性が強いので、全部まとめて説明します。以下のように3行のテキストデータをTextStreamに読み込んだものとします。
図19
まず「Column」は、各行に於ける列番号です。行の先頭が1となり、全角・半角は関係なく「1文字で1」ずつ進みます。
なお改行マーク(CrLf)の場合は、「Crで1文字」「Lfで1文字」となります。
「Line」は、行番号です。改行マークの次から新しい行となり、行が変わると共にColumnが1から再スタートします。
「AtEndOfLine」は行末か否かを表しますが、改行マークがCrLfの場合は「Crの直前」「Lfの直前」の2か所でTrueとなります。
またTextStreamの末尾(=文末)も行末の一種ですので、Trueとなります。
「AtEndOfStream」は文末か否かを表します。TextStreamの末尾に達したらTrueとなります。
図19で使われている改行マークは「CrLf」でしたが、「Lfのみ」や「Crのみ」でも改行と判断してくれるO/Sやアプリが存在します(例:Windows付属のメモ帳)。
まず「Lfのみ」を改行マークとしたテキストをTextStreamに読み込んだ場合が以下です。
内容は図19と同じですが、Crが無いので「Lfの直前」のみでAtEndOfLineがTrueとなります。
図20
一方「Crのみ」を改行マークとしたテキストをTextStreamに読み込んだ場合が以下です。
図21
改行マークが「Cr」の場合、TextStreamは「Crを改行マークとは認識しない」事がわかります。改行と認識されないため、Lineプロパティは「どこまで行っても1」であり、Columnプロパティも増え続けることになります。
「Crのみの改行」はMacが使用しているらしいので、注意が必要です。
4.TextStreamのメソッド
TextStreamオブジェクトを操作するメソッドとして、下記のものがあります。なお入出力モードの設定等によっては使用できないメソッドもありますので、下表の右列に使用可の条件を併記しました。
メソッド | 内容 | ①Open ②GetFile | ③Create (書込専用) | |||
---|---|---|---|---|---|---|
iomode=1 (読取専用) | iomode=2 (書込専用) | iomode=8 (追記専用) | ||||
Read | TextStreamから指定文字数を読取 | 〇 | ||||
Read | TextStreamから1行を読取 | 〇 | ||||
Read | TextStreamから全体を読取 | 〇 | ||||
Skip | 指定文字数をスキップ | 〇 | ||||
Skip | 1行をスキップ | 〇 | ||||
Write | 指定文字列をTextStreamに書込み | 〇 | 〇 | 〇 | ||
Write | 指定文字列+改行をTextStreamに書込み | 〇 | 〇 | 〇 | ||
Write | 改行文字をTextStreamに書込み | 〇 | 〇 | 〇 | ||
Close | TextStreamを閉じる | 〇 | 〇 | 〇 | 〇 |
なお、以下では「ファイルポインタ位置から・・・」という説明が多くあります。Read系(読み取り)やWrite系(書き込み)の操作を行えば、ファイルポインタはその分後方に移動しますが、ユーザー側で「ファイルポインタ位置」を動かすのはSkipメソッドとSkipLineメソッドの2つです。しかも後方にしか移動できませんし、読み取り時にしか使えません。
もし前方に移動したい場合は、再度OpenTextFileなどでTextStreamを開く必要がありますし、文字を上書きしたい場合は、VBA内で重ね書きをするしかなさそうです。
ADODB.Streamなどと比較すると機能が少なく見えますが、何より動きは完全に文字数単位ですし、読み取り・書き込みのモードもキッパリと分かれているので、機械的な作業に向いているオブジェクトかもしれません。
但し「カンマ区切り」のデータなどにはTextStreamの機能だけでは対応できず、データベース向きでは無さそうです。
(カンマ区切りは、1文字ずつ読み取り・判断をする方法も考えられますが、行単位のデータとして一旦取り込んでからVBA内で分割作業をする方法の方が簡単な気がします。)
4-1.Read
Readメソッドは、テキストを読み込んだTextStreamの「ファイルポインター位置から指定文字数分」を読み取り、その文字列を戻します。構文は以下です。TextStream.Read (characters) As String
引数(characters)の内容は以下です。
引数 | 型 | 内容 | ||
---|---|---|---|---|
1 | characters | 必須 | Long | 読み込む文字数(0以上)を指定。 TextStreamの末尾を超えた文字数を指定した時には、TextStream末尾までを戻す。 ゼロを指定すると「値ゼロの文字列(vbNullString)」を戻す。 |
なおReadメソッドを使用するには、図22で示したように「入出力モード(iomode)をForReading(値=1 既定値)」にしておく事が必要です。
Readメソッドを使ったコード例を以下に示します。
- '========== ⇩(6) Readメソッドで指定文字数分を読み取る ============
- Sub test04()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 1, True, 0) '←TextStreamの作成
- MsgBox Fst.Read(6) '←テキスト読み取り
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
137行目「Set Fst = Fso.OpenTextFile(FN, 1, True, 0)」で、ファイルFNの内容をTextStreamに格納しています。第2引数を値=1(ForReading)に指定していますので、読み取り専用で開いています。また第4引数を「0(TristateFalse)」に指定していますので、文字コード「Shift-JIS」で読み込んでいます(当然、読み込むファイルはShift-JISで無いと、文字化けします)。
139行目「MsgBox Fst.Read(6)」では、Readメソッドで文字数指定(ここでは6文字)でデータの読み取りをしています。
このコードを図で示したのが下図です。ここでは3行のテキストデータを使用しています。
図25
テキストファイルからTextStreamにデータ格納した時点では、ファイルポインターは「先頭」にあります。Readメソッドで読み取る文字は「ファイルポインターを始点」としますので、ここでは「先頭から6文字」となります。文字数には改行マークも入り、CrLfの場合は2文字とカウントされます。
4-2.ReadLine
ReadLineメソッドは1行ずつ文字列を読み取り、その文字列を戻します。なお、ファイルポインタより後方の文字列が対象となるのは同じです。構文は以下で、引数はありません。TextStream.ReadLine As String
コード例は以下です。テキストファイルを開いた後、1行ずつ読み取り、メッセージとして出力しています。
- '========== ⇩(7) ReadLineメソッドで各行のデータを読み取る ============
- Sub test05()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 1, True, 0) '←TextStreamの作成
- Do Until Fst.atEndOfStream = True '←文末が来るまで繰り返し
- MsgBox Fst.Readline '←1行分を読み取り
- Loop
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
167行目「Set Fst = Fso.OpenTextFile(FN, 1, True, 0)」でテキストファイル(FN)を開きます。
169~171行目のDo~Loopでは、条件式「Fst.atEndOfStream = True」が成立するまで(=文末になるまで)処理を繰り返します。
繰り返している処理は170行目「MsgBox Fst.Readline」で、1行ずつ読み取ってはメッセージ表示するという内容です。
なお「1行」をどのように認識するか ですが、図19・図20のように、TextStreamでは「CrLf」と「Lf」を改行マークと認識しています。ですので1行分のデータは、改行マーク「CrLf または Lf」で区切られます。
図26のコードを図で示すと、下図のようになります。
図27
なお1行ずつ取得したデータ内には、「Cr」も「Lf」も付いていません。これは、ReadLineは「現在のファイルポインタの位置から、AtEndOfLine値がTrueとなった所まで」を1行のデータと見なして出力すると共に、その先の「AtEndOfLine値がFalseになる場所」までファイルポインタを移動させ、次行の始点としている と推測されます。
4-3.ReadAll
ReadAllメソッドはTextStreamの文末までを読み取り、その文字列を戻します。なお、ファイルポインタ(=現在位置)よりも後方の文字列が対象となります。構文は以下で、引数はありません。TextStream.ReadAll As String
コード例は以下です。テキストファイルを開いた後、全文をメッセージとして出力しています。
- '========== ⇩(8) ReadAllメソッドで全データを読み取る ============
- Sub test06()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 1, True, 0) '←TextStreamの作成
- MsgBox Fst.ReadAll '←全データの読み取り
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
197行目「Set Fst = Fso.OpenTextFile(FN, 1, True, 0)」でテキストファイル(FN)を開きます。開いた直後のファイルポインタは先頭にあります。
199行目「MsgBox Fst.ReadAll」で、現在のファイルポインタ(ここでは先頭)から文末までの「全データ」を読み取り、メッセージとして出力しています。
図28のコードを図で示すと、下図のようになります。
ReadAllメソッドでは、改行マークも何も関係なく、文末(AtEndOfStream=True)までの範囲のデータを読み取ります。
図29
4-4.Skip
Skipメソッドは「指定された文字数をスキップ」します。構文は以下です。TextStream.Skip (Characters)
引数(Characters)の内容は以下です。
引数 | 型 | 内容 | ||
---|---|---|---|---|
1 | characters | 必須 | Long | スキップする文字数(0以上)を指定。 TextStream末尾を超えた文字数の指定時は、TextStream末尾までポインタを移動。 0を指定すると、ファイルポインタは動かず |
引数は「スキップする文字数」で、「1バイト文字でも2バイト文字でも、1文字」と数えます。設定可能な値は0以上の整数値(Long型)です。
なお、TextStreamの末尾を超えた文字数を指定した時には、ファイルポインタ位置を「TextStream文末」に指定(AtEndOfStream=True)した事になります。
また、ファイルポインタが文末(AtEndOfStream=True)にある時に、SkipメソッドやRead系メソッドを実行すると「ファイルにこれ以上データがありません」との実行時エラーになります。但し「Skip(0)」や「Read(0)」ではファイルポインタを動かさないのでエラーとはなりません。
なお、このSkipと下のSkipLineは、入出力モード(図08)がForReading(読み取り専用)の時にしか使用できません。読み取り不可モードで使用するとエラーとなります。
Skipメソッドを使ったコード例は以下です。
- '========== ⇩(9) Skipメソッドで指定文字数分をスキップした後、読み取る ============
- Sub test07()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 1, True, 0) '←TextStreamの作成
- Fst.Skip (2) '←ポインタの移動
- MsgBox Fst.Read(6) '←テキスト読み取り
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
227行目「Set Fst = Fso.OpenTextFile(FN, 1, True, 0)」でファイル内容をTextStreamに格納した時点では、ファイルポインタは先頭にあります。
そこから229行目「Fst.Skip (2)」を実行すると、ファイルポインターは「2文字分」後ろ側に移動します。下図の例で言えば、1行目の「あ」の直前の位置です。
その状態で230行目「MsgBox Fst.Read(6)」を実行すると「ファイルポインタの位置から6文字」を読み取り、メッセージとして出力することになります。
図31のコードを図で表すと以下のようになります。図25と比較するとSkipメソッドの機能が分かり易いと思います。
図32
4-5.SkipLine
SkipLineメソッドは「1行分をスキップ」します。構文は以下で、引数はありません。TextStream.SkipLine
なお、現在のファイルポインタが文末(AtEndOfStream=True)の時にSkipLineメソッドを実行すると、もうスキップするものがありませんので「ファイルにこれ以上データがありません」との実行時エラーになります。またスキップする1行をどのように認識するかは、ReadLineの時と同じで「CrLf または Lf」までとなります。
SkipLineメソッドを使ったコード例は以下になります。
- '========== ⇩(10) SkipLineメソッドで1行スキップした後、読み取る ============
- Sub test08()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 1, True, 0) '←TextStreamの作成
- Fst.SkipLine '←1行分をスキップ
- Do Until Fst.atEndOfStream = True '←文末が来るまで繰り返し
- MsgBox Fst.Readline '←1行分のテキスト読み取り
- Loop
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
257行目「Set Fst = Fso.OpenTextFile(FN, 1, True, 0)」でファイル内容をTextStreamに格納した時点では、ファイルポインタの位置は先頭にあります。
259行目「Fst.SkipLine」で、1行分スキップすることで、ファイルポインタは2行目の先頭に移動します。
261~263行目のDo~Loopでは、「Fst.atEndOfStream = True」となるまで(=文末になるまで)処理を繰り返します。
処理内容は図26の170行目と同じで、1行ずつ読み取ってはメッセージ表示するというものです。
図33のコードを図で示すと、下図のようになります。
図34
SkipLineメソッドは、「次に現れる改行マークが終了した位置」にファイルポインタを移動させる機能です。また最終行では文末でファイルポインタは停止します。
ですので、SkipメソッドやReadメソッド実行後で「行頭以外の位置にファイルポインタ」があっても、SkipLineを実行すれば「次の行頭に頭出し」が出来ることになります。
4-6.Write
Writeメソッドは、指定した文字列をTextStreamに書き込みます。構文は以下です。TextStream.Write (text)
引数(text)の内容は以下です。
引数 | 型 | 内容 | ||
---|---|---|---|---|
1 | text | 必須 | String | 書き込む文字列を指定。 |
なおWrite系のメソッドを使う場合は、入出力モード(iomode)をForWriting(値=2)またはForAppending(値=8) にしておく必要がありあます。
まず、入出力モードを「ForWriting(値=2)」にして書き込みを行うコード例が下記です。
- '========== ⇩(11) Writeメソッドでの書き込み ============
- Sub test09()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 2, True, 0) '←TextStreamの作成
- Fst.Write "Aアあ" '←文字列の書き込み
- Fst.Write "123" '←文字列の書き込み
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
287行目「Set Fst = Fso.OpenTextFile(FN, 2, True, 0)」では、TextStreamを開いています。第2引数に「2(=ForWriting)」を指定しているので「書き込み専用」となり、開いた時には「空のTextStream」となっています。
289行目「Fst.Write "Aアあ"」ではWriteメソッドを使って、空のTextStreamに対して文字列を書き込んでいます。
290行目も、追加で文字列をTextStreamに書き込んでいます。
289~290行目では2つのWriteメソッドで文字列を書き込んでいますが、双方で指定した文字列同士は「スペースや一定の文字で区切られず」続けて書き込まれます。
そして292行目「Fst.Close」で、TextStreamを閉じることで、ファイルに書き込んだ文字列が確定します。
図36を図で示すと、下図のようになります。
図37
一方、入出力モードを「ForAppending(値=8)」にして既存ファイルに追記を行うコード例が下記です。
図36で作られたファイルに対して、追記を行っています。
- '========== ⇩(12) Writeメソッドでの追記 ============
- Sub test10()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 8, True, 0) '←TextStreamの作成
- Fst.Write "かきく" '←文字列の追記
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
317行目「Set Fst = Fso.OpenTextFile(FN, 8, True, 0)」では、テキストファイルを読み込み、TextStreamを開いています。第2引数に「8(ForAppending)」を指定しているので「追記モード」となり、書き込みの為のファイルポインタは文末に位置します。
319行目「Fst.Write "かきく"」では、既存のテキストの末尾に、文字列を追加しています。
図38を図で示すと、下図のようになります。
図39
4-7.WriteLine
WriteLineメソッドは、指定文字列+改行マークをTextStreamに書き込みます。構文は以下です。TextStream.WriteLine ([text])
引数(text)の内容は以下です。
引数 | 型 | 内容 | ||
---|---|---|---|---|
1 | text | 省略可 | String | 書き込む文字列を指定。 省略または「""(長さゼロの文字列)」を指定時は、改行のみが書き込まれる。 |
WriteLineメソッドを使用したコード例は以下です。3つの文字列をファイルに書き込んでいますが、最初の2つをWriteLineにすることで「3行のデータ」となります。
- '========== ⇩(13) Writeメソッドでの書き込み ============
- Sub test11()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 2, True, 0) '←TextStreamの作成
- Fst.WriteLine "Aアあ" '←文字列+改行の書き込み
- Fst.WriteLine "123" '←文字列+改行の書き込み
- Fst.Write "かきく"
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
349行目「Fst.WriteLine "Aアあ"」で、文字列「"Aアあ"」に続けて「改行マーク(CrLf)」を書き込んでいます。
350行目「Fst.WriteLine "123"」で、文字列「"123"」に続けて「改行マーク(CrLf)」を書き込んでいます。
351行目「Fst.Write "かきく"」は、文字列「"かきく"」のみを書き込みます。
図41のコードを図で表したのが下図です。
図42
なお改行マークには「CrLf」「Lf」「Cr」がありますが、WriteLineメソッドで書き込む改行マークは「CrLf」になります。これはWindows上で実行しているから というよりは、FileSystemObjectそのものがWindows用なので改行の仕様はCrLfということではと思われます。
4-8.WriteBlankLines
WriteBlankLinesメソッドは、指定された数の改行マークをTextStreamに書き込みます。構文は以下です。TextStream.WriteBlankLines ([lines])
引数(lines)の内容は以下です。
引数 | 型 | 内容 | ||
---|---|---|---|---|
1 | lines | 必須 | Long | 書き込む改行マークの数(0以上の整数) なお0を指定すると、何もしない事と同等。 |
引数(lines)の数だけ改行マーク(CrLf)を書き込みますが、ゼロを指定した時には改行無し(=何も書き込まない)ことになります。
WriteBlankLinesメソッドを使ったコード例は以下です。
- '========== ⇩(14) WriteBlankLinesメソッドで改行の書き込み ============
- Sub test12()
- Dim Fso As Object '←FileSystemObjectの変数宣言
- Dim Fst As Object '←TextStreamの変数宣言
- Const FN As String = "Test_SJIS.txt"
- Set Fso = CreateObject("Scripting.FileSystemObject") '←FileSystemObjectの生成
- Set Fst = Fso.OpenTextFile(FN, 2, True, 0) '←TextStreamの作成
- Fst.Write "Aアあ"
- Fst.WriteBlankLines (2) '←改行の書き込み
- Fst.Write "かきく"
- Fst.Close
- Set Fst = Nothing
- Set Fso = Nothing
- End Sub
379行目「Fst.Write "Aアあ"」では、改行なしの文字列をTextStreamに書き込んでいます。
380行目「Fst.WriteBlankLines (2)」では、2個の改行マークをTextStreamに書き込んでいます。
381行目「Fst.Write "かきく"」では、改行なしの文字列をTextStreamに書き込んでいます。
これを図で示すと、下図のように「2行目が空のテキスト」となります。
図45
なお改行には「CrLf」「Lf」「Cr」が存在しますが、WriteBlankLinesメソッドではWindows標準の「CrLf」となります。
また、既存テキストを追記モード(ForAppend:値=8)で開き、WriteLineやWriteBlankLinesで改行を追加しても、追加した改行はCrLfとなりますが、元のテキストの改行は「そのまま」となります。
例えば元のテキスト中の改行マークが「Lf」だとするとそのLfはそのまま残り、新しく追加する改行は「CrLf」となるのです。つまり「種類の異なる改行マークが混在」する事になりますので、注意が必要です。
4-9.Close
Closeメソッドは、開いているTextStreamオブジェクトを閉じます。構文は以下で、引数はありません。TextStream.Close
OpenTextFileやOpenAsTextStream、CreateTextFileの各「TextStreamを開くメソッド」でTextStreamオブジェクトを開き、作業が完了した時点でCloseメソッドで閉じます。
しかし他サイトでも指摘されているように、Closeメソッドを実行しなくても「TextStreamを開くメソッド」が書かれているプロシージャを抜けてしまえば、ファイルは解放(書き込みモード時はファイル保存)され、Closeを実行したのと同じ結果が得られます。
しかしVBAの機能におまかせするのでは無く、自分で開いたものは自分で閉じるという姿勢がプログラム全体のミス防止・分かり易さにもつながると思いますので、「必ずCloseで閉じる」ことをお勧めします。
アプリ実例・関連する項目
「CSVファイルの読み込み」「祝日を自動反映するカレンダー」
サンプルファイル
今回、説明の中で紹介したコードは、以下のサンプルファイルの標準モジュール(Module1)に記載しています。実行するにはVBEから直接実行してください。また読み書きするテキストファイル名にはPathを指定していないので、既定のドキュメントフォルダー
「C:¥Users
フォルダーにファイルが無い場合に読み込みを行うとエラーとなりますので、事前にテキストファイルを作成してから試してください。その場合、ファイル名と併せて文字コードも合わせる必要があります。
テキストファイルの読み書き_TextStream編(its-051.xlsm)
セキュリティ向上を目的として「インターネット経由でダウンロードしたOfficeファイル(Excel等)のマクロは、既定でブロック」されるようにOfficeアプリケーションの既定動作が変更になりました。(2022年4月より切替開始) 解除の方法については「ダウンロードファイルのブロック解除方法」を参照下さい。 |