テキストファイルの読み書き(ADODB.Stream編)
- 1.ADOライブラリ
- 1ー1.ADOオブジェクトの種類
- 1ー2.ADOオブジェクト生成
- 1ー2ー1.事前バインディング
- 1ー2ー2.実行時バインディング
- 2.ADODB.Streamのプロパティ
- 2-1.Type
- よりみち(テキスト/バイナリが自動認識)
- 2-2.CharSet
- 2-3.LineSeparator
- 2-4.Position
- 2-5.Size
- 2-6.EOS
- 2-7.Mode
- 2-8.State
- 3.ADODB.Streamのメソッド
- 3-1.Open
- 3-2.LoadFromFile
- 3-3.SaveToFile
- 3-4.ReadText
- 3-5.WriteText
- 3-6.Read
- 3-7.Write
- 3-8.SkipLine
- 3-9.SetEOS
- 3-10.CopyTo
- よりみち(CopyToで置き換えられる文字数)
- 3-11.Close
- 3-12.Flush
- 3-13.Cancel
- アプリ実例・関連する項目
- サンプルファイル
VBAを使い「テキストファイルを読み書き」する手法について、以下のようなシリーズで紹介しています。
①テキストの文字コードについて
②Openステートメント法による読み書き
③ADODB.Stream法による読み書き ←今回
④TextStream法による読み書き
⑤XMLHTTP法によるWebデータの読み取り (作成中)
⑥Web上からダウンロードしての読み取り (作成中)
今回紹介する「ADODB.Stream法」は、下表のように様々な文字コードに対応できる手法です。
なお操作可能なのは、LAN上(ドライブ名やサーバー名で始まるパス名の場所)のファイルのみです。
読み書き手法 | 対応文字コード | ファイルの場所 | ||||
---|---|---|---|---|---|---|
Shift-JIS | UTF-8 | UTF-16 | LAN上 | Web上 | ||
② | Openステートメント | 〇 | △ | 〇 | ||
③ | ADODB.Stream | 〇 | 〇 | 〇 | 〇 | |
④ | TextStream | 〇 | 〇 | 〇 | ||
⑤ | XMLHTTP | 〇 | 〇 | 〇 | 〇 |
手法全体の話ですが、図01のように手法ごとに「対応できる文字コードが異なる」原因の1つとして、手法の生まれた時期と関係がありそうです。下図は各手法の歴史です。今回紹介するADODBは1996年から存在したことが分かります。
図02
同様に、各文字コードの歴史をまとめたのが下図です。ADODBが生まれた頃には、Shift-JISはもちろんUnicodeも既に存在しているので、主な文字コードには対応させたようです。
図03
以下では、ADO および ADODB.Streamオブジェクトについて説明します。
1.ADOライブラリ
ADOは「ActiveX Data Objects」の略で、Microsoftが提供する「外部データベースに接続」するためのツールです。この接続可能なデータベースには、データベースアプリである「SQL Server」や「Access」、またワークシートをテーブル相当として使えるExcelなどがあります。
これに加えて、テキストの並びをテーブル相当として使える「テキストファイル(CSVファイル)」も対象となりますし、普通のテキスト読み書き処理もその機能の一部として使えるようです。
まずADOライブラリの概要とそのオブジェクト生成法について整理します。なお「Stream」はオブジェクト名で、その頭についている「ADODB」は、オブジェクトを提供するアプリケーションの名前になります。
1ー1.ADOオブジェクトの種類
ADO直下には多くのオブジェクトが存在します。図04はそのオブジェクトが、ADOのバージョンにより使えるか否かと、実行時バインディング時にVBAのCreateObjectで生成可能か否かをまとめたものです。オブジェクト (カッコはコレクション) | 内容 | 事前バインディング | 実行時 | |
---|---|---|---|---|
Ver 2.8 | Ver 6.1 | Create | ||
Command | データソースに対して実行する特定コマンドの定義 | 〇 | 〇 | 〇 |
Connection | データソースへの接続 | 〇 | 〇 | 〇 |
Error | 発生するアクセスエラーの詳細情報 | 〇 | 〇 | 〇 |
(Errors) | 〇 | 〇 | × | |
Field | データ列 | 〇 | × | × |
(Fields) | 〇 | × | × | |
Parameter | Commandオブジェクトに関連付けられた引数 | 〇 | 〇 | 〇 |
(Parameters) | 〇 | 〇 | × | |
Property | ADOの動的特性 | 〇 | 〇 | × |
(Properties) | 〇 | 〇 | × | |
Record | Recordsetの行、又はファイルシステム内のディレクトリやファイル | 〇 | 〇 | 〇 |
Recordset | コマンド実行により返された結果 | 〇 | 〇 | 〇 |
Stream | データの流れ | 〇 | 〇 | 〇 |
今回のStreamオブジェクト(一番下)には使えないバージョンはありませんし、またCreateObjectでも生成可能です。
1ー2.ADOオブジェクト生成
ADOのオブジェクトを使うには、まずオブジェクトを生成する必要があります。その生成方法には以下の2種類があります。・「事前バインディング」・・・コードを実行する前に生成
・「実行時バインディング」・・コードを実行した時に初めて生成
事前バインディングの特徴は、コード作成時にインテリセンス(コード補完機能=使用可能なプロパティ等が表示され、選択することが可)が使える事と併せ、効率が良い(≒実行速度が速い)ことです。
一方実行時バインディングは、実行しているPCに合わせたライブラリを呼び出すため、PC環境の異なる複数ユーザーにマクロを配布する時でも安全度は高いと思われます。
1ー2ー1.事前バインディング
事前バインディングでは、VBE(コードを書くウィンドウ)上部の「ツール」→「参照設定」から、図05のように「Microsoft ActiveX Data Objects X.X Library」にチェックをし有効にします。図05
X.Xの部分はバージョンを表していますが、最新Excelでは「2.8」①までと、少し飛んで「6.1」②が選択できるかと思います。
通常は「最新のもの」を選択するのが良いとされています。しかし複数のPCを対象に動かすようなシステムの場合に「最新バージョン」を選択して作ってしまうと、もしその「最新バージョン」がインストールされていないPCが存在する場合は、うまく動かない可能性が出てきます。ライブラリを選択する際には「どのPCにも入っているバージョン」を選ぶのが良いと思います。
また図04のFieldオブジェクトのように「使用できないバージョン」もあるので、事前確認が必要です。
プログラム的には、図05のように「ADOのライブラリーを参照設定」した上で、図06のように「オブジェクト変数をADOオブジェクトとして宣言」→「New句を使って生成」した後、オブジェクト変数を処理に使用します。
- '========== ⇩(1) 事前バインディングでの宣言と生成 ============
- Dim ADOst As ADODB.Stream '←Streamオブジェクト型変数の宣言
- Set ADOst = New ADODB.Stream '←Streamオブジェクトの生成
- (生成したStreamオブジェクト変数を使い、テキストやバイナリの処理)
01行目「Dim ADOst As ADODB.Stream」では、Streamオブジェクト型で変数宣言をします。
03行目「Set ADOst = New ADODB.Stream」では、New句を使ってオブジェクトを生成します。
オブジェクト生成後は、そのオブジェクト変数(図06では、ADOst )を使って処理作業を行います。
1ー2ー2.実行時バインディング
実行時バインディングの場合は、使用するオブジェクトをCreateObjectを使って生成します。- '========== ⇩(2) 実行時バインディングでの宣言と生成 ============
- Dim ADOst As Object '←一般オブジェクト型変数の宣言
- Set ADOst = CreateObject("ADODB.Stream") '←Streamオブジェクトの生成
- (生成したStreamオブジェクト変数を使い、テキストやバイナリの処理)
11行目「Dim ADOst As Object」では、単なるObject型として変数宣言をします。
13行目「Set ADOst = CreateObject("ADODB.Stream")」では、CreateObject関数を使ってオブジェクトを生成します。オブジェクト生成後は、そのオブジェクト変数(図07では、ADOst )を使って処理作業を行います。
なお今回のサンプルコードは、全て実行時バインディングでオブジェクト生成をしています。
2.ADODB.Streamのプロパティ
Streamオブジェクトのプロパティには、以下があります。プロパティ | 内容 | 既定値 |
---|---|---|
Type | データの種類 | adTypeText(値=2) |
CharSet | 文字セット | "Unicode" |
LineSeparator | 行区切り文字 | adCRLF(値=-1) |
Position | 現在の位置 | 0(データ先頭位置) |
Size | ストリームのバイトサイズ | 0 |
EOS | 現在位置が末尾か否か(True/False) | - |
Mode | 可能なデータ変更権限 | adModeUnknown(値=0) |
State | Objectの開閉状態 | adStateClosed(値=0) |
2-1.Type
このTypeプロパティをどの値にするかで、ADODB.Streamオブジェクトを「テキストデータ」の読み書きツールとして使うのか、「バイナリデータ」のツールとして使うのかが決まります。下記の2種があります。定数 | 値 | 内容 |
---|---|---|
adTypeBinary | 1 | バイナリデータ |
adTypeText | 2 | テキストデータ(既定) |
なお表内の定数は「ADOライブラリを参照設定」した時にのみ通用します。参照設定をしない場合は「値」列の数値を使用するか、事前に定数宣言等をして下さい(以降のプロパティ/メソッドも同じです)。
このプロパティ値は、設定/取得が可能ですが、「設定」は以下の条件の時のみです。
・StreamをOpenしていない状態
・Openしていて、Positionが先頭(Position=0)にある時(Streamが空である必要は無い)
またTypeプロパティ値により、Streamオブジェクト内で使えるプロパティ・メソッドが以下の様に制限されます。
(下記以外は、どちらでも使えます)
プロパティ/メソッド | Type= | ||
---|---|---|---|
1(バイナリ) | 2(テキスト) | ||
プロパティ | CharSet | × | 〇 |
LineSeparator | × | 〇 | |
メソッド | Read | 〇 | × |
ReadText | × | 〇 | |
Write | 〇 | × | |
WriteText | × | 〇 | |
SkipLine | × | 〇 |
Type=2(既定値)に設定すると、Stream内でデータ(バイト値)をテキストとして扱うために「文字コード(CharSetプロパティ)」や「改行マーク(LineSeparatorプロパティ)」が必要となりますが、バイナリの場合(Type=1)には不要です。ですので、Type=1ではCharSet・LineSeparatorを設定しようとすると「このコンテキストでは操作は許可されていない」とのエラーが発生します。
メソッドについても同様で、データを読み書きするためのRead/Writeメソッドはバイナリ用、ReadText/WriteTextメソッドはテキスト用と使い分けるようになっています。また、改行までの1行分をスキップするSkipLineメソッドも、改行の概念があるテキストのみのメソッドです。
(テキスト/バイナリが自動認識) Microsoftのサイトでは「新しい空のStreamにバイナリデータを書き込むと、Typeプロパティ値はadTypeBinary(値=1)になる」と記載されていますが、色々試してみても再現できません("バイナリデータを書き込む" という意味が理解できません)。 テキスト/バイナリが自動認識されるとすると、それはスゴイ事ですが、勝手に変わられても困まる気がします。 |
2-2.CharSet
CharSetプロパティは、読み書きするテキストファイルの文字コードです。既定値は「Unicode」で、これは「UTF-16LE(BOM付)」のことです。値の取得/設定が可能ですが、「設定」は以下の条件の時のみです。
・StreamをOpenしていない状態
・Openしていて、Positionが先頭(Position=0)にある時(Streamが空である必要は無い)
このPosition=0の時に「異なる文字コードに変更」したとしても、既にStream内に存在していた文字(=バイト列)が変わってくれる訳ではありません。文字コードを変更した時点から「Streamに格納する文字を、設定した文字コードで管理する」という意味です。
つまり、元の文字コードのデータを残したまま「文字コードを変更」した場合、新たに格納する文字量が多ければ「元の文字を全て上書き」してくれますが、元の文字コードでの文字量が多かった場合には「新たな文字の後ろ側に元の文字コードのバイトが残る」ことになります。現象としては「後ろ側が文字化け」します。
これを防ぐために、文字コードを途中で変更する場合は「SetEOSメソッドでStream内をクリア」することが重要と思います。
CharSetに指定できる値(ワード)は、基本的には下図のように「レジストリのリスト」に載っている「文字コード」ワードのようです。(但し、指定するとエラーになるものも存在します)。
図11
なお、このレジストリの中に「"UTF-16"」は存在しませんが、試してみるとCharSetプロパティに設定可能です。もちろん、上記で説明したCharSet既定の「Unicode」と同じ役目を果たしてくれます。
そこでレジストリに載っているものに加え「"UTF-16"」のようなもっともらしい名前をCharSetに指定し、どの文字コードに対応するかを試してみた結果が以下になります。なお文字コードはWindows付属のメモ帳で選択できる範囲+αに絞りました。
Charset= | UTF-8 | UTF | UTF | Shift | ||||
---|---|---|---|---|---|---|---|---|
BOM無 | BOM付 | BOM無 | BOM付 | BOM無 | BOM付 | |||
レ ジ ス ト リ 値 | _autodetect | ◎ | ||||||
csShiftJIS | ◎ | |||||||
csWindows31J | ◎ | |||||||
ms_Kanji | ◎ | |||||||
shift_jis | ◎ | |||||||
shift-jis | ◎ | |||||||
unicode | 〇 | ◎ | 〇 | |||||
unicode-1-1-utf-8 | 〇 | ◎ | ||||||
unicode-2-0-utf-8 | 〇 | ◎ | ||||||
unicodeFFFE | 〇 | ◎ | 〇 | |||||
utf-8 | 〇 | ◎ | ||||||
x-ms-cp932 | ◎ | |||||||
x-sjis | ◎ | |||||||
x-unicode-2-0-utf-8 | 〇 | ◎ | ||||||
思 い つ き | utf-16 | 〇 | ◎ | 〇 | ||||
utf-16le | 〇 | ◎ | 〇 | |||||
utf-16be | ◎ | △ | ||||||
sjis | ◎ |
図12
試してみて「文字コードに対応する」という意味には2通りあることがわかりました。
1つ目は「Stream内での文字データの管理の方法」で、図12の範囲では「◎印」で示している4種類です(図13)。
文字コード | BOM有無 | CharSetの指定例 | |
---|---|---|---|
① | UTF-8 | 付 | utf-8 |
② | UTF-16LE | 付 | unicode |
③ | UTF-16BE | 無 | utf-16be |
④ | Shift-JIS | - | shift-jis |
各文字コードの特徴は「文字コード編」を参照して頂くとして、例えば "Unicode" をStreamのCharSetに指定すると、Stream内ではテキストをUTF-16LE(BOM付き)で管理する事になります。つまり「半角でも全角でも2バイト/文字」+「先頭2バイトにはBOM(0xFFFE)が入る」という状態です。
一方 "UTF-8" を指定すると、「半角アルファベット+数字は1バイト、半角カナや全角文字は3バイト」+「先頭3バイトにはBOMが入る」状態になります。
もう1つは「テキストファイルを正しく読み込めるか否か」で、図12では「〇印」や「△印」で示しています。テキストファイルを読み込むには「LoadFromFile」メソッドを使用します。
もちろん、テキストファイルの「UTF-8」「UTF-16」「Shift-JIS」などの基本的な文字コードは合っていないとダメです。その上で、テキストファイル側の文字コード環境(バイト順やBOM有無)が「Stream内の文字コード環境に受け入れられるのか」という問題のようです。
図13の4項目をもう少し詳しく説明します。なお①~④の番号は、図13の番号と合わせています。
①StreamがUTF-8(BOM付き)の場合は、テキストファイルがBOM無しでも正しく取り込んでくれるようです。なおStreamに取り込んだ後は、BOM付きとして管理されます。
②StreamがUTF-16LE(BOM付き)の場合、バイト順がLE(Little-Endian)であればBOM無しでも正しく取り込んでくれます。この場合もStream内ではBOM付きとして管理されます。
一方バイト順がBE(Big-Endian)のテキストファイルは、BOMが付いていれば正しく取り込みます。これはBOM情報を見て、バイト順を修正しながら取り込んでいるのだと考えられます。ですのでBOMが付いていないUTF-16BEは、文字化けします。
③StreamがUTF-16BE(BOM無し)の場合、BOM付きのUTF-16BEのテキストファイルを読み込むと「先頭のBOMを何かの文字と判断」してしまうようで、先頭のみ文字化けとなります(図12の△印)。LE(Little-Endian)の場合は全くダメでした。
④Shift-JISではBOMも無く、UTFのような難しさは無さそうです。
なお図12でも分かるように、同じ文字コードでも複数の「文字コード」ワードが選択できる事が分かります。しかし、PCの環境により使えない場合も考えられますので確認は必要と思います。
また、なぜ「レジストリに載っていない文字コードワードが有効なのか?」ですが、おそらくADODB.Streamの実行ファイルに書き込まれていると考えるしかないのですが、詳細は調べ切れず、図12の下側のような「思いつき」のレベルに留まったのは残念です。
そのため図13以外の「UTF-8(BOM無し)」「UTF-16LE(BOM無し)」「UTF-16BE(BOM有り)」で管理するためのCharSetワードは、残念ながら見つけられていませんし、「UTF-32」もエラーとなるので、UTF-32ファイルを開けるのかも不明のままです。
Stream内にデータを取り込むには、上記のLoadFromFile以外に「WiteText」メソッドを使用する方法がありますが、WriteTextでは素直に「Stream内の文字コード環境に合わせて格納」される事になります。
2-3.LineSeparator
「LineSeparator」プロパティは、改行マークの設定です。以下の3種が選べます。定数 | 値 | 内容 |
---|---|---|
adCRLF | -1 | vbCrLf相当(既定) |
adLF | 10 | vbLf相当 |
adCR | 13 | vbCr相当 |
このLineSeparator値には2つの役割があります。
1つ目が「ReadTextメソッドで1行ずつ読み込む(引数にadReadLine(値=-2)を指定)時の、行を分ける改行マーク」の判別です。
改行コードには「CrLf(0x0D 0A)」「Lf(0x0A)」「Cr(0x0D)」と3種あります。Excelでは主にCrLfとLfが使われますが、下図のようにメモ帳上ではCrでも改行と見なしてくれます。
(作り方:バイナリエディタで改行コードを操作した後、再度メモ帳で開いています。)
図15
そのため下表のように、テキストの中で使われる改行コードに合わせて「LineSeparator」の値を設定する必要があります。
改行コード | CrLf | Lf | Cr |
---|---|---|---|
adCRLF(既定) | 〇 | × | × |
adLF | (〇) | 〇 | × |
adCR | (〇) | × | 〇 |
テキストの改行が「CrLf」の場合は、上表の通りLineSeparator値に何を指定しても行単位で切り出してくれます。しかしカッコ付きとしているのは「カス」が残るからです。例えばCrLfに対し「adLF」を指定すると文字列の最後に「Cr」が残ってしまいますし、「adCR」を指定すると文字列の先頭に「Lf」が残る事になるので注意が必要です。
この場合、もし残ったCrやLfを削除するのであれば、Replace関数で「""(長さゼロの文字列)」に変換をします(Trim関数では削除できません)。
2つ目が「WriteTextメソッドの第2引数に『adWriteLine(値=1)』を指定した時の、テキストに付加される改行マーク」です。例えば以下のコードのように、LineSeparator値を変えながら改行を指定してみます。
- '========== ⇩(3) WriteTextでの改行コード ============
- Private Sub test01()
- Dim ADOst As Object
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .LineSeparator = -1 '←CrLf
- .WriteText "abc", 1 '←①
- .LineSeparator = 10 '←Lf
- .WriteText "def", 1 '←②
- .LineSeparator = 13 '←Cr
- .WriteText "ghi", 1 '←③
- .savetofile "Test_SJIS.txt", 2
- .Close
- End With
- Set ADOst = Nothing
- End Sub
30行目「.LineSeparator = -1」では、LineSeparatorプロパティにadCRLF(値=-1 :図14参照)を指定していますので、改行コードは「CrLf」となります。その改行コードで31行目「.WriteText "abc", 1」を実行し、文字列"abc"の最後に改行コードを追加しています。
33行目「.LineSeparator = 10」は改行コードとしてLfを、36行目「.LineSeparator = 13」はCrを追加しています。
このコードでの結果は以下のようになります。
図18
メモ帳上(図18の左側)ではどれも同じ「改行」に見えますが、バイナリエディタ(図18の右側)で見ると、改行のコードがそれぞれ異なる事が分かります。このように、WriteTextメソッドで付加する改行コードにもLineSeparatorプロパティ値が効いてきます。
ちなみにメモ帳の改行コードは「Windows(CRLF)」と表示されていますが、これは先頭の改行コードから判断しているようです。メモ帳の改行コード表示を鵜呑みにしない事も大切です。
なお、LoadFromFileでテキストファイルをStreamに読み込む際、「テキストファイル側の改行マークをStream側のLineSeparator値に合わせる」事はしないようです。例えば、既定のLineSeparator(=CrLf)のStreamに、Lf を改行マークとしたファイルを読み込んでも、Stream内では Lf のままとなります。ですので事前にファイル側の改行マークを確認し、その改行マークにStream設定を合わせる等の対応が必要となります。
2-4.Position
「Position」プロパティはStream内での現在位置を示し、OpenメソッドでStreamを開いている間のみ設定・取得が可能です。Position値はStreamの先頭をゼロとした「バイト単位」の値で、設定の場合は「Stream内にあるデータのバイト数以下(0 ~ Stream.Size値)」にする必要があります。
テキストの読み書き(ReadText/WriteText)は、現在のPosition位置以降が処理の対象となります。しかし、例えば「ある文字と文字の間にPositionを移動しよう」とすると、結構大変な作業になります。
その理由として、テキスト型でStreamを扱う場合、設定した文字コード(CharSet値)で文字データは管理されるからです。つまり文字コードによって文字当たりのバイト数が変わってきますし、またBOMが付いたり付かなかったりもするので、目的のPosition位置を算出するには手間が掛かります。
下記は様々な文字コード環境で、「"abあい12"(半角2文字+全角2文字+数字2個)」という文字をStreamに格納した状態を表しています。
図19
例えば「bの後ろ」にPositionを移動したい場合、設定した文字コードによって、全て異なる値の指定が必要と分かります。
もしそのようなPosition指定が必要な場合は、一旦VBA内に文字列として読み込み、処理をしてから再度Streamに戻す方が良さそうです。
また、バイナリの読み書き(Read/Write)やCopyToでのデータコピーにもPosition値が効いてきます。Positionを先頭や末尾(Stream.Size)以外の場所に移動させての操作には、慎重な位置計算が必要です。
2-5.Size
Sizeプロパティは、Stream内のデータのサイズを「バイト数」で戻します。値の取得のみ可能です。CharSetの所でも説明しましたが、同じ文字列を読み込んでも、Stream内で管理する文字コードによりSize値が異なります。
下記のように、書き込み位置を文末に移動する場合などに使われます。
- '========== ⇩(4) Positionを移動して、既存ファイルに追記 ============
- Private Sub test02()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt" '←読み書きするテキストファイル
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2 '←テキストとして処理
- .Charset = "Shift-JIS" '←テキストファイルの文字コードを指定
- .Open
- .LoadFromFile FN '←テキストファイルからデータを読み込み(Positionはゼロのまま)
- .Position = .Size '←Positionを文末に移動
- .WriteText "abc" '←Positionの位置から文字をStreamに書き込み(=追記)
- .SaveToFile FN, 2 '←ファイルに書き出し
- .Close
- End With
- Set ADOst = Nothing
- End Sub
71行目「.LoadFromFile FN」では、テキストファイルをStream内に読み込んでいます。
Stream内に読み込んだ段階では「Positionは先頭」に居ますので、このまま文字を書き込むと「先頭側から上書き」をしてしまうことになります。ですので72行目「.Position = .Size」で、Position位置を「文末(=Sizeプロパティ値)」に移動します。
Positionを文末に設定したら、73行目「.WriteText "abc"」で文字列を追加し、74行目「.SaveToFile FN, 2 」でテキストファイルにデータを戻しています。
この流れを図にしたのが以下になります。ここでは、テキストファイルに「"abあい12"」という文字が書かれており、その末尾に「"abc"」を追加しています。
図21
なお、WriteTextで文字列を加えた時には「Position値は加えた文字列の末尾に移動」しますので、更にWriteTextで文字を加える時には、あえてPositionを移動させる必要はありません。
2-6.EOS
EOSプロパティは「End Of Steam」の略で、現在位置(=Position値)がStreamの末尾にあるか否かを True/False で示します。値の取得のみ可能です。Openした直後は「Streamには何も無い」ので、「現在位置=文末」ということになり、EOS値はTrueとなっています。それ以降は、読み書きのステップごとに変わりますので、以下にまとめました。
なお下図の「Streamの状態」は、Stream内に文字列が格納されている状態を表し、且つ緑色の縦線は現在位置(Position値)を示しているつもりです。また読み書きする文字列は、"abc" の3文字としています。
図22
Openした後、読み取り時(図22の上段)は、テキストファイルの内容をLoadFromFileメソッドで読み取ります。読み取るとファイルの文字はStreamに格納されますが、現在位置(Position値)は移動せず「文末では無くなる」ために、EOS値はFalseとなります。
Streamに入っている文字を読み取るにはReadTextメソッドを使います。ReadTextは「Positionの位置から後方」を読み取ります。読み取る文字数は引数指定でき「読み取った文字の末尾」へPositionは移動します。ですので全文字を読み取った場合は、Position=文末となり「EOS値はTrue」となります。
一方書き込み時(図22の下段)は、WriteTextメソッドを使って文字列をStreamに書き込みます。WriteTextは「Positionの位置から」文字を書き込み、「書き込んだ文字列の末尾」にPosition位置は移動します。ですので空のStreamに書き込んだ場合は、EOS値はTrueとなります。
Streamに書き込んだ文字をファイルに出力するにはSaveToFileメソッドを使用します。出力してもStream内の文字は消えませんが、現在位置はStream先頭に移動しますので、EOS値はFalseとなります。
2-7.Mode
Modeプロパティは、データ変更権限の設定です。取得/設定が可能ですが、設定はStreamを開く前(Openメソッドの実行前)にしかできないようです。値の内容は以下のリストのようになっており、値としては0~27が設定可能(組み合わせが可能?)なようです。しかし、その組み合わせ方は良く分かりません。
定数 | 値 | 内容 |
---|---|---|
adModeUnknown | 0 | アクセス許可未設定 |
adModeRead | 1 | 読み取り専用のアクセス許可 |
adModeWrite | 2 | 書き込み専用のアクセス許可 |
adModeReadWrite | 3 | 読み取りと書き込みのアクセス許可 |
adModeShareDenyRead | 4 | 他ユーザーが読み取りアクセス許可で接続を開くことを禁止 |
adModeShareDenyWrite | 8 | 他ユーザーが書き込みアクセス許可で接続を開くことを禁止 |
adModeShareExclusive | 12 | 他ユーザーが接続を開くことを禁止 |
adModeShareDenyNone | 16 | 他ユーザーが任意のアクセス許可で接続を開くことを許可 |
上表の内容を見ると「扱うファイルへの操作制限を掛ける事が可能」に思えますが、試してみると違いました。自プロセスでMode値を設定後、自プロセス・他プロセスから、どんな操作が可能かを調べた結果が下記です。(〇=操作可、×=操作不可)
Mode値 | 1 | 2 | 3 | 4 | 8 | 12 | 16 | |
---|---|---|---|---|---|---|---|---|
自 プ ロ セ ス | Open | 〇 | 〇 | 〇 | 〇 | 〇 | 〇 | 〇 |
Load | × | 〇 | 〇 | 〇 | 〇 | 〇 | 〇 | |
Read | 〇 | × | 〇 | 〇 | 〇 | 〇 | 〇 | |
Write | × | 〇 | 〇 | 〇 | 〇 | 〇 | 〇 | |
Save | 〇 | × | 〇 | 〇 | 〇 | 〇 | 〇 | |
他 プ ロ セ ス | Open | -/〇 | 〇/- | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 |
Load | -/〇 | 〇/- | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | |
Read | -/〇 | 〇/- | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | |
Write | -/〇 | 〇/- | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | |
Save | -/〇 | 〇/- | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 |
図24
Mode=1では、ファイルがLoad出来ず、またStreamに書き込むことが出来ません。しかしファイルへ書き込みは可。
Mode=2では、ファイルはLoad出来るが読み込めず、Streamへの書き込みは可なのにファイルへの書き込みは不可。
それ以外のMode値では、特に支障なく操作可能なようです。
また他プロセスでは、ファイルへの読み書きが制限されるような事はありませんでした。なお「-/〇」等としているのは、自プロセス側でファイルをLoadやSaveが出来ないことを表しています。
この結果から考えると、Modeプロパティは「ConnectionオブジェクトやRecordオブジェクトで使われる場合にアクセス制限を掛ける」もののようです。そのため、今回のようにテキストファイルの読み書きとしてStreamオブジェクトを使う場合には、既定の「adModeUnknown(値=0)」のままとしておいた方が良さそうです。
2-8.State
Stateプロパティは、オブジェクトの状態を示します。取得のみ可で、以下の内容です。なおMicrosoftでは「値の組み合わせとなる場合もあり」と説明しています。
定数 | 値 | 内容 |
---|---|---|
adStateClosed | 0 | 閉じている(既定) |
adStateOpen | 1 | 開いている |
なおテキスト操作で試した結果では、以下のようにStateが「 0 or 1 」以外となる条件は見つけられませんでした。
・StreamをCloseしている(Openしていない)・・・State = 0
・StreamをOpenしている・・・・・・・・・・・・State = 1
恐らくこのStateは「データベース接続・操作」で使う事を前提としているようなので、図25も「テキスト操作に不要そうな値は、見え消し」としておきました。
3.ADODB.Streamのメソッド
Streamオブジェクトのメソッドは以下の内容になります。メソッド | 内容 |
---|---|
Open | Streamオブジェクトを開く |
LoadFromFile | ファイルからStreamに読み込む |
SaveToFile | Streamの内容をファイルに保存 |
ReadText | テキストデータをStreamから読み込む |
WriteText | テキストデータをStreamに書き込む |
Read | バイナリデータをStreamから読み込む |
Write | バイナリデータをStreamに書き込む |
SkipLine | 1行全体をスキップ |
SetEOS | Streamの終わりの位置を設定 |
CopyTo | Streamの内容を別のStreamにコピー |
Close | Streamオブジェクトを閉じ、リソースを解放 |
Flush | バッファの残データを全て吐き出す(≒ファイル書き込みを完了させる) |
Cancel | ファイルの呼び出しを取り消す |
なおMicrosoftのサイトを見ると、Streamオブジェクトには「Statメソッド」が存在するようにも見えます。Statメソッドは「Streamの情報を取得」するもので、その情報とは「Streamの名前・サイズ・作成や変更、アクセスの時刻」等との事です。
しかし、Statメソッドは実際のライブラリの中には存在しません。
その代わりとして、サイズは「Sizeプロパティ」で取得できます。一方で、Streamの時刻関係の取得は不可能です。
3-1.Open
Openメソッドは、Streamオブジェクトを開きます。構文は以下です。Stream.Open [,Source][,Mode][,OpenOptions][,UserName][,Password]
パラメータとしては、下記5個が設定可能とのMicrosoftの説明です。
パラメータ | 省略可否 | 内容 | 既定値 | |
---|---|---|---|---|
1 | Source | 省略可 | Streamのデータソース | (省略=Streamを開く) |
2 | Mode | 省略可 | Streamのアクセスモード | ad |
3 | Open | 省略可 | 開く際のOption | ad (=既定Option?で開く) |
4 | User | 省略可 | StreamオブジェクトのユーザーID | 無し |
5 | Pass | 省略可 | Streamオブジェクトのパスワード | 無し |
5個のパラメータの中には選択肢が複数存在するものもあります。しかし試してみると、Streamオブジェクトでは、全パラメータを規定値に設定した「Stream.Open , 0, -1, "", "" (途中のパラメータを省略してもOK)」のみが実行可能です。これ以外は「引数が間違った型、許容範囲外、または競合している」との実行時エラーとなります。
従ってテキストやバイナリを扱うStreamオブジェクトでは、パラメータ無し(=既定値)でOpenメソッドを実行するのが一般的で、他のサイトでも「Stream.Open」のみでStreamを開いています。
3-2.LoadFromFile
LoadFromFileメソッドは、ローカルファイルの内容をStreamに読み込みます。構文は以下です。Stream.LoadFromFile Filename
パラメータFilenameには、ファイルのパス+ファイル名を指定します。パス名無しでファイル名を指定した場合には、既定のドキュメントフォルダ「C:¥Users¥[User名]¥Documents¥」の場所にあるファイルと見なされるようです。
なお、そこに指定したファイルが存在しない場合は、エラーとなります。
LoadFromFileの特徴は2つです。
・Streamをクリアした後、ファイル内容をStreamに読み込む
・読み込み後のPositionは先頭(Position = 0)
図で表すと以下のようになります。
図28
Streamが空の状態(図28の左側)でも、また何か入っている状態(図28の右側)でも、LoadFromFile後は「読み込んだファイル内容のみ」となり、且つPositionの位置は先頭(ゼロ)となります。
LoadFromFileメソッドを使用したコード例は以下になります。
- '========== ⇩(5) ファイルからStreamに読み込む ============
- Private Sub test03()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .LoadFromFile FN
- MsgBox .ReadText
- .Close
- End With
- Set ADOst = Nothing
- End Sub
98行目「.Charset = "Shift-JIS"」では、Stream内での文字コード管理をShift-JISとしています。もちろん読み込むテキストファイル側もShift-JISとしないと、Stream内で文字化けが発生することになります。
101行目「.LoadFromFile FN」では、テキストファイル(変数FN)をStream内に読み込んでいます。
102行目「MsgBox .ReadText」では、そのStream内のデータを取り出してメッセージ出力しています。なおStreamはLoadFromFileで読み込んだままの状態なので、Position位置は先頭(Position=0)にあるため、ReadTextでは「全データ」を読み出す事ができます。
下記は図29のコードで、「AアあBC」というテキストが入っているファイルをLoadFromFileで読み込み、そのStream内のデータをReadTextで取り出しています。
図30
3-3.SaveToFile
SaveToFileメソッドは、Streamの内容を「ファイルに書き出し」ます。書き出す時の文字コードは、CharSetで指定したStreamの文字コードです。構文は以下になります。Stream.SaveToFile FileName [, SaveOptions]
2つのパラメータの内容は以下です。
パラメータ | 内容 | |||
---|---|---|---|---|
1 | FileName | 必須 | 保存先のパス+ファイル名 | |
2 | SaveOptions | 省略可 | adSaveCreateNotExist (値=1)既定 | 指定ファイルが存在しない場合は新作 指定ファイルが存在する場合はエラー |
adSaveCreateOverWrite (値=2) | 指定ファイルが存在しない場合は新作 指定ファイルが存在する場合は上書き(元データは消える) |
第1パラメータのパス名を省略すると、既定のドキュメントフォルダ「C:¥Users¥[User名]¥Documents¥」の場所にあるファイルと見なされるようです。
第2パラメータに「値=1を指定(=省略)」した場合に既存のファイルが存在すると「ファイルへ書き込めませんでした」との実行時エラーとなります。
なお、第2パラメータに「値=2を指定」した場合に既存のファイルが存在すると、書き出す先のファイルに元々書かれていたデータは全て削除され、上書きしたデータのみが保存されます。
SaveToFileでは、StreamのPosition位置がどこにあろうとも「Streamの内容が全てファイルに出力」されます。また出力後は、Position位置は先頭(Position=0)に移動します。
SaveToFileメソッドを使用したコード例は以下になります。
- '========== ⇩(6) Streamの内容をファイルに出力 ============
- Private Sub test04()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .WriteText "AアあBC"
- .SaveToFile FN, 2
- .Close
- End With
- Set ADOst = Nothing
- End Sub
131行目「.WriteText "AアあBC"」では、文字列「"AアあBC"」をStreamに書き込んでいます。
132行目「.SaveToFile FN, 2」では、ファイル(変数FN)にStreamの内容を書き込んでいます。書き込むテキストの文字コードは、128行目「.Charset = "Shift-JIS"」で指定したShift-JISになります。
また下図のように、StreamオブジェクトのPositionがどこにあっても「全データが出力」され、且つ出力後はPositionが先頭に移動します。
図33
3-4.ReadText
ReadTextメソッドは、Streamオブジェクトから「テキストデータ」を読み取り、戻り値として出力します。なお読み取る範囲は「現在位置(Position値)から後ろ側」となります。構文は以下です。Stream.ReadText( [NumChars] ) As String
引数(NumChars)には以下を指定します。
定数 | 値 | 内容 |
---|---|---|
adReadAll | -1 | 全てを読み取る(既定) |
adReadLine | -2 | 1行を読み取る |
- | Long型の値 | 読み取る文字数 |
図34
ReadTextで読み取った後のPositionは「読み取った文字列の最後」に移動します。つまり、以下のように分かれます。
・全データの読み取り時は、Positionは文末に移動(.Position=.Size、.EOS=True」
・行単位での読み取り時は、改行マークの次(=次行の先頭)、または文末に移動
・文字数単位での読み取り時は、読み取った文字列の最後に移動
まず「adReadAll(値= -1:既定)」の場合は、現在の読み書き開始位置(Position値)からStreamの最後までを読み取り、読み取ったテキストをString型として戻します。コード例は以下になります。
- '========== ⇩(7) ReadTextで全てを読み取る ============
- Private Sub test05()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .LoadFromFile FN
- MsgBox .ReadText(-1)
- .Close
- End With
- Set ADOst = Nothing
- End Sub
161行目「.LoadFromFile FN」でテキストファイルからデータをStream内に取り込み、そのデータを162行目「.ReadText(-1) 」で全て読み出し、メッセージとして出力しています。
なお、ファイルからLoadFromFileで取り込んだ直後は、読み書き開始位置はデータ先頭にあります(Position = 0)ので、Streamの全データが一度に出力されることになります。
以下の図は、改行を含んだテキストを読み込み、ReadText(-1)で出力する様子です。
図36
上図の場合は「Position値が先頭」にあるため、全データが出力されます。またReadText実行後のPosition値は文末(=EOS)に移動します。
なお、ReadText実行前にPosition値を移動した場合には、その位置から文末までが出力されます。
次に「adReadLine(値= -2)」の場合は、読み書き開始位置(Position値)から、その行の終わりまでを読み取り、読み取ったテキストをString型として戻します。この時、改行マークは戻しません。コード例は以下になります。
- '========== ⇩(8) ReadTextで1行ずつ読み取る ============
- Private Sub test06()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .LineSeparator = -1
- .Open
- .LoadFromFile FN
- Do Until .EOS = True
- MsgBox .ReadText(-2)
- Loop
- .Close
- End With
- Set ADOst = Nothing
- End Sub
189行目「.LineSeparator = -1」では、Stream内の改行マークとしてCrLf(値=-1:既定)を指定しています。
192行目「.LoadFromFile FN」でテキストファイルからデータをStream内に取り込みます。
195行目「.ReadText(-2) 」では、「現在の読み書き開始位置から改行マーク直前まで」のデータを取り出し、メッセージとして出力しています。ですので「改行マークでテキストを分割」して「連続して出力」をするために、194行目「Do Until .EOS = True」で文末(EOS=True)が来るまで195行目を繰り返しています。
ファイルからLoadFromFileで取り込んだ直後は、読み書き開始位置(Position値)はデータ先頭(Position = 0)にありますが、改行マークまでの1行分を取り出した後は、「改行マークのすぐ後ろ側(=次の行頭)」または「文末」に移動します。
そのため再びReadText(-2)が実行された際には、次の行頭から行末までのデータが取り出されます。
以下の図は、改行を含んだテキストを読み込み、ReadText(-2)で各行データを出力する様子です。
最も右側の「EOS=True」の状態まで達すると、出力するデータは無いのでDo~Loopを抜け出すようにしています。
図38
なお1行ずつ読み取る場合に「その行の読み取りをスルー」したい場合には、下で説明する「SkipLine」メソッドを使います。
ReadTextメソッドの引数に「文字数」を指定した場合には、「現在の読み書き開始位置(Position位置)から指定文字数分」のデータを取り出します。コード例は以下になります。ここでは「読み書き開始位置を2バイト後ろに移動」させ、「7文字分」を出力させています。
- '========== ⇩(9) ReadTextで文字数を指定して読み取る ============
- Private Sub test07()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .LoadFromFile FN
- .Position = 2
- MsgBox .ReadText(7)
- .Close
- End With
- Set ADOst = Nothing
- End Sub
221行目「.LoadFromFile FN」でファイルを読み込んだ直後は、Positionは先頭にあるのですが、222行目「.Position = 2」で2バイト後方に移動させています。お気付きの様に、移動させる単位が「バイト」なので、文字の切れ目を狙うとすれば「Streamに設定された文字コードによって異なる」事になります(図19参照)。
223行目「MsgBox .ReadText(7)」では、「Position位置から、指定した7文字」を取り出してメッセージとして出力します。この文字数の中には改行マークも含まれ、今回のCrLfだと2文字とカウントされます。
この図39の動きを図にすると以下のようになります。ここでは、改行と全角を含んだ文字列をファイルから読み込んでいます。
出力後のPositionの位置は、読み取った文字の最後(スタート位置+指定文字数)の位置となります。
図40
Streamから文字列を切り出す場合、図39のように「Position」+「ReadText(文字数)」を使用すると、文字コードを考慮しなければならなくなるのはデメリットです。
そこで、一旦「VBA内の文字列」にしてから「VBAのMid関数など」を使って文字列を切り出す手法が以下です。条件は図39と同じです。
- '========== ⇩(10) 全データからMid関数等を使って切り出す ============
- Private Sub test08()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Dim Str As String
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .LoadFromFile FN
- Str = .ReadText(-1) '全データを取得
- MsgBox Mid(Str, 3, 7) '文字列として切り出す
- .Close
- End With
- Set ADOst = Nothing
- End Sub
252行目「.LoadFromFile FN」でテキストファイルを読み込んだ後、253行目「Str =.ReadText(-1)」で全データを変数Strに代入しています。
変数Strにはデータは「文字列」として格納されていますので、254行目「MsgBox Mid(Str, 3, 7)」のように使い慣れたVBAのMid関数などを使用することで、文字列を切り出す事が可能です。
尚この場合でも、改行マーク(CrLf)は2文字とカウントします。
3-5.WriteText
WriteTextメソッドは、文字列をPositionの位置からStreamに書き込みます。構文は以下です。Stream.Writetext Data [,Options]
2つのパラメータの内容は以下になります。
パラメータ | 内容 | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Data | 必須 | Streamに書き込む文字列 | |||||||||
2 | Options | 省略可 |
|
第1パラメータ(Date)には、Streamに書き込む文字列(変数の場合はString型)を指定します。
第2パラメータ(Options)には、書き込む文字列の後ろに「改行マーク」を付けるか否かの設定です。既定(省略した場合)は改行を付けませんが、adWriteLine(値=1)を指定した場合は「LineSeparatorプロパティで設定した改行マーク」を1つ追加します。
なお、WriteTextで文字をStreamに書き込んだ後は、Position位置は「書き込んだ文字の後ろ側」に移動します。
WriteTextを使ったコード例は以下です。Streamに「"abcdefg"」を書き込み、行末に改行を入れます。続けて「"ABC"」を書き込みます。
- '========== ⇩(11) StreamにWriteTextでデータを書き込む ============
- Private Sub test09()
- Dim ADOst As Object
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .WriteText "abcdefg", 1
- .WriteText "ABC", 0
- .Position = 0
- MsgBox .ReadText(-1)
- .Close
- End With
- Set ADOst = Nothing
- End Sub
280行目「.WriteText "abcdefg", 1」で1行目の文字列を書き込み、最後に改行を入れています。
WriteTextでStreamに書き込んだ後は、Position位置は「書き込んだ文字列の最後」に移動しますので、WriteTextを連続して実行することで文字列が、後ろに後ろにと追加されます。
281行目「.WriteText "ABC", 0」では、2行目の文字列を書き込んでいます。こちらには改行を入れていません。
データを連続してStreamに書き込んだ後は、Position位置は文末になります。このStreamデータをReadTextで読み取る際には「Positionよりも後方が対象」となりますので、282行目「.Position = 0」で先頭にPositionを移動させてから283行目「MsgBox .ReadText(-1)」で読み出しています。
図で表すと以下のようになります。
図44
このように連続してWriteTextを実行すれば何の問題は無いのですが、途中で異なるメソッドを実行させる時には「Positonの位置」に注意が必要です。例えば、途中でSaveToFileでファイルにテキストを保存する場合が以下のコードです。
- '========== ⇩(12) WriteTextでデータを書き込む前にPositionを変更 ============
- Private Sub test10()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .Open
- .WriteText "abcdefg", 1
- .SaveToFile FN, 2
- .WriteText "ABC", 0
- .Position = 0
- MsgBox .ReadText(-1)
- .Close
- End With
- Set ADOst = Nothing
- End Sub
311行目「.WriteText "abcdefg", 1」でStreamに文字を書き込んだ後、312行目「.SaveToFile FN, 2」で一旦Stream内容をファイルに書き込んでいます。その後再び313行目「.WriteText "ABC", 0」でStreamに文字を書き込んでいます。
「SaveToFile」でも説明したように「SaveToFile実行後は、Positionは先頭に移動」します。一方でWriteTextはPositionの位置から書き込みを始めますので、Streamの先頭から上書きしていく形になります。
図で示すと以下のようになります。
図46
図46の一番右側では、先頭部が重ね書きされてしまっています。今回はShift-JISなので、半角文字=1バイトでしたが、全角(2バイト)が混ざっていると文字化けが発生する可能性もあります。またUTF-8だと1文字当たりのバイト数が1~4バイトと幅があるので、文字化けする可能性も高まります。
なお先頭から上書きする場合でも、WriteTextで書き込む文字の量(Size)がStreamの既存文字量(Size)以上であれば、全てが置き換わる事になります。
もしSaveToFile等でPositionが移動してしまう場合は、「.Position = .Size」でPositionを文末に移動させるか、SetEOSメソッドで「Streamをクリア」した後、作業を続ける必要があります。LoadFromFileの場合もファイル読み込み後はPositionは先頭にありますので、そのままWriteTextを使うと「先頭から上書き」となる為、同様の注意が必要です。
なお図46の場合、最終的なテキストの末尾にはCrLfが付いているのですが、なぜかメッセージBoxの最後には「改行が表現されていない」事が分かります。MsgBox関数の仕様なのでしょうが、この現象について詳しい事は分かりませんでした。
3-6.Read
ReadメソッドはStreamから「バイトデータ」を読み取り、戻り値として「Byte型の配列」を返します。なおこのReadメソッドを使う際は、Typeプロパティ値をadTypeBinary(Type = 1)にしておく必要があります。構文は以下です。Stream.Read(Numbytes) As Variant
引数(Numbytes)には、以下を指定できます。なお、読み取りスタート位置は「Position位置」です。
定数 | 値 | 内容 |
---|---|---|
adReadAll | -1 | 全バイトを読み取る(既定) |
- | Long型の値 | 読み取るバイト数 |
Microsoftサイトを含む他のサイトでは「1行分を読み取る(値=-2)」が有効であるかのような説明をしています。しかし指定すると実行時エラーが発生するので、図47では見え消しにしています。
Readメソッドを使ったコード例は以下です。テキストファイルから文字列を読み込み、Streamから1バイトずつ取得してその値を16進数でイミディエイトWindowに出力しています。謂わば、読取専用の「バイナリエディタ」みたいな役目を果たします。
- '========== ⇩(13) Streamの各バイト値をReadで読み取る ============
- Private Sub test11()
- Dim ADOst As Object
- Dim Str() As Byte '←Readの戻り値を入れる配列
- Const FN As String = "Test_SJIS.txt"
- Dim i As Long
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 1
- .Open
- .LoadFromFile FN
- Str = .Read(-1)
- For i = LBound(Str, 1) To UBound(Str, 1)
- Debug.Print WorksheetFunction.Dec2Hex(Str(i), 2) & vbTab;
- Next i
- .Close
- End With
- Set ADOst = Nothing
- End Sub
まず339行目「.type = 1」で、Streamを「バイナリ型」で管理させます。
342行目「.LoadFromFile FN」では、Shift-JISのテキストファイルからデータをStreamに取り込んでいます。ファイルを取り込んだだけなので、Positionは先頭にあります。
343行目「Str = .Read(-1)」では、Positionの位置から文末まで(今回の場合は、Streamの全バイト)を読み取り、そのバイナリデータを「Byte型の配列」として戻してきます。そのため333行目「Dim Str() As Byte」でByte型の動的配列を宣言しています。なお動的配列のサイズを事前に決定しておく必要は無く、343行目で読み取ったバイト数と同じ要素数の配列に自動的にサイズ変更されます。
図48のコードの流れを図で表すと、以下のようになります。ここでは、全角・改行を含んだテキストを読み込ませています。
図49
なお今回は「複数バイト」を読み取るために「配列」が必要なようにも見えますが、単一の「1バイトのみ」の読み取りの場合でも「Byte型の動的配列宣言」+「動的配列に読み取りバイト値を戻す」という図48と同様の手順が必要です。1バイトのみの場合は「1要素の配列」となります。
読み取ったバイト値が入った配列の処理ですが、今回は345~347行目のFor~Nextで、1バイトずつ(=1要素ずつ)値をイミディエイトWindowに出力させています。1要素に入っているバイト値は1バイト分なので、値はByte型の「0~255(10進数)」ですが、図49の左下のバイナリエディタの値(=16進数)と合わせるために、10進数→16進数の変換をしています。
その変換部分が、346行目の「WorksheetFunction.Dec2Hex(Str(i), 2)」です。
10進数→16進数の変換方法としては、VBAの「Hex関数」も存在します。しかし変換後の2桁目がゼロの場合(0x00~0x0F)は、「1桁目の値(0~9,A~F)」しかHex関数は戻しません。例えば改行のCrLfは「0x0D」「0x0A」ですが、「D」「A」しか戻してくれないのです。
この2桁表示方策としては、以下のような方法が良く紹介されています。
・「 = Right("0" & HEX(10進数), 2)」のように先頭に"0"を付け、右から2文字を切り出す。
もちろんこの方法でも良いのですが、Excel VBAのWorksheetFunctionには表示桁数を指定できるDec2Hex関数がありますので、ここではそれを使用しています。
なおセル内で使用する関数「Dec2Hex」を使って「Evaluate("Dec2Hex( " & Str(i) & ",2)")」としてもOKです。
3-7.Write
Writeメソッドは、Streamにバイトデータを書き込みます。なお、Writeを使用するにはTypeプロパティ値をadTypeBinary(Type = 1)にしておく必要があります。構文は以下です。Stream.Write Buffer
指定するパラメータ(Buffer)は以下になります。
パラメータ | 内容 | |
---|---|---|
Buffer | 必須 | 書き込むバイト配列の入ったVariant型の値を指定 |
パラメータの内容がピンと来ないと思いますので、Writeを使ったコード例を以下に示します。
Streamに対し「"abあい" + 改行」と「"12"」のバイト値を連続して書き込み、最後にファイルに出力しています。
- '========== ⇩(14) StreamにWriteでバイト値を書き込む ============
- Private Sub test12()
- Dim ADOst As Object
- Dim Str() As Byte
- Const FN As String = "Test_UTF16.txt"
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 1
- .Open
- Str = "abあい" & vbCrLf
- .Write Str
- Str = "12"
- .Write Str
- .SaveToFile FN, 2
- .Close
- End With
- Set ADOst = Nothing
- End Sub
373行目「Dim Str() As Byte」では、Readの時と同様に「Byte型の動的配列」を宣言しています。
378行目「.type = 1」では、Streamをバイナリ型で管理しています。
381行目「Str = "abあい" & vbCrLf」では、文字列「"abあい"+改行」をバイト値として配列Strに格納しています。VBA内では文字列は「UTF-16LE」で管理されていますので、半角全角を問わず「2バイト/文字」となります。
382行目「.Write Str」では、381行目で作成したByte型の配列StrをStreamに書き込んでいます。
384~385行目も同様に、Streamに「"12"」の文字列のバイト値を書き込んでいます。
387行目「.SaveToFile FN, 2」では、Streamのデータをファイルに出力しています。図51のコードの流れを図で示すと、以下のようになります。
図52
書き込んだテキストファイルは、メモ帳の右下にも表示されている通り、「UTF-16LE」の文字コードで保存されています。つまりStream内には「UTF-16LE」として文字列のバイト値が書き込まれている事が分かります。なお、テキスト型のUTF-16LEでStreamが管理されている訳では無いので、先頭にはBOMはありません。
今回は「文字列→バイト値」に変換してからStreamに書き込んでいますが、直接バイト値(0~255)を書き込んでもOKです。
3-8.SkipLine
SkipLineメソッドは次の改行マークまでの1行分の文字を飛び越え(スキップ)ます。構文は以下で、パラメータはありません。Stream.SkipLine
このメソッドは、Streamが既定のテキスト型(Type=2 )の時に使用できます。また行の終わりを示す改行マークはLineSeparatorプロパティ値で判断されます。
SkipLineについて別な言い方をすれば「次に出現する改行マークの後ろまでPositionを移動」させる機能です。ですので行毎に読み取り(ReadText(-2) )をする場合だけでなく、各行の行頭部分だけを取り出したり、タイトルを除いた全行の取り出しなどにも使えます。
以下に示したSkipLineを使ったコード例では、タイトル行のみスキップし、それ以降を1行ずつ取り出しています。
- '========== ⇩(15) SKipLineで読み込み行を制御する ============
- Private Sub test13()
- Dim ADOst As Object
- Const FN As String = "Test_SJIS.txt"
- Dim i As Integer
- Dim Data As Variant '←カンマで分割したデータを入れる配列
- Set ADOst = CreateObject("ADODB.Stream")
- With ADOst
- .type = 2
- .Charset = "Shift-JIS"
- .LineSeparator = -1
- .Open
- .LoadFromFile FN
- .SkipLine
- Do Until .EOS = True
- Data = Split(.ReadText(-2), ",")
- Debug.Print Data(0), Data(1), Data(2)
- Loop
- .Close
- End With
- Set ADOst = Nothing
- End Sub
今回読み込むファイルは、下図の左側のように「1行目タイトル行+2行目以降にデータ」が並んでいるものとし、1行には「3つのデータがカンマ区切り」で並んでいます。
414行目「.LoadFromFile FN」で、そのファイルを読み込み、まず415行目「.SkipLine」で1行スキップします。このことで「StreamのPositionは、2行目の先頭に移動」することになります。
417~420行目のDo~Loopでは、Streamの末尾(EOS)が来るまで「1行ずつ」処理を行っています。コードの流れを図で表したのが下図です。
図54
Do~Loop内の処理内容をもう少し詳しく説明します。415行目で1行目はスキップしていますので、処理は2行目から最後までという事になります。
まず418行目「Data = Split(.ReadText(-2), ",")」では、「.ReadText(-2)」で1行分を読み取ります。1行は3つのデータがカンマで区切られていますので、「カンマを目印」にしてSplit関数で分割します。分割されたデータは配列(要素番号はゼロ始まり)になりますので、Variant型の変数Dataで受け取ります。
419行目「Debug.Print Data(0), Data(1), Data(2)」では、3つの配列要素をイミディエイトWindowに出力しています。
3-9.SetEOS
SetEOSメソッドは、Streamの末尾の位置(EOS)を、現在のPositionプロパティの位置に設定するものです。構文は以下で、設定パラメータはありません。
Stream.SetEOS
上記説明では良く分からないと思うので、下図を例に説明します。Shift-JISのテキストがStreamに入っているとします。
図55の①はSize=6バイトのStreamですが、Positon=4に設定した後「SetEOSメソッド」を実行すると、②のようにPositionの位置がEOS(Streamの末尾)となります。この時、Position=4よりも後ろにあったデータ(ここでは「"B"」と「"C"」)は削除されます。
また③のように、Position=0に設定した後「SetEOSメソッド」を実行すると、Streamのサイズはゼロ(=空の状態)となります。
図55
不要部分を削除したり、StreamオブジェクトをCloseせずに空にする場合には必要な機能です。
3-10.CopyTo
CopyToメソッドは、Streamの内容を別のStreamにコピーします。構文は以下です。Stream.CopyTo DestStream [, NumChars]
パラメータは2つあり、以下の内容になります。
引数 | 内容 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | DestStream | 必須 | コピー先のStreamオブジェクト | |||||||||||||
2 | NumChars | 省略可 |
コピー範囲の指定
|
第1パラメータ(DestStream)は、コピー先のStreamオブジェクトです。このCopyToメソッドを実行する時点では、コピー先StreamはOpenメソッドで開かれている必要があります。
なおMicrosoftのサイトでは、基本的には同じ型のStream同士(テキスト型→テキスト型 または バイナリ型→バイナリ型)でのデータコピーを推奨していますが、図61のようにBOM削除などの時には「テキスト型→バイナリ型」を使う事は可能です。但し「バイナリ型→テキスト型」では実行時エラーが発生します。
第2パラメータ(NumChars)は、コピーする範囲で、「-1」を指定(または省略)すれば、全データをコピーする事になります。但しコピー元StreamのPosition位置から後方が対象です。
「整数値」を指定した場合には、指定した文字数(またはバイト数)をコピーします。この時もコピー元のStreamのPosition位置から後方が対象です。なお、指定した文字数(またはバイト数)が、Stream末尾を超えた値の場合は、末尾までを指定(= -1を指定したの同等)した事になります。
まず基本的な動きを下図で説明します。CopyToを使ってStream1(上段)の内容をStream2(下段)にコピーしています。
なおCopyToのパラメータは、①②が「-1(全データコピー)」、③が「指定文字数(またはバイト数)指定」としています。
図57
図57の左側①は、コピー先のStream2が空の場合です。Stream1のPosition位置から末尾までのデータがStream2にコピーされます。ここではPosition=0となっているため、全データがコピーされます。
なお、CopyTo実行後のコピー元(Stream1)のPositionは、コピーしたデータ範囲の最後に移動します。ここでは全データ(パラメータに「-1」を指定)が範囲なので、Stream1の末尾になります。コピー先(Stream2)の実行後のPosition位置は、データ貼付け範囲の末尾となります。
図57の中央②は、コピー先のStream2にはデータが入っている場合です。Stream2のPosition位置は末尾にありますので、Stream1のデータがStream2の末尾に追加される形となります。コピー元・コピー先のCopyTo実行後のPosition位置は、データ末尾になります。
図57の右側③は、コピー元・コピー先ともPosition位置がデータの中間に居る場合で、且つコピー範囲が指定文字数(または指定バイト数)の場合です。ここでは1文字を指定しています。
まずコピー元(Stream1)のPositionは「"BC"」の直前にあり、且つコピーは1文字なので、コピー対象は「"B"」の1文字ということになります。一方コピー先(Stream2)のPositionは「"YZ"」の直前にあります。この状態でCopyTo(1)を実行すると、コピー先(Stream2)の「"Y"」の部分をStream1の「"B"」で置き換えますが、Stream2の最後の「"Z"」はそのまま残ります。
またコピー先のPositionも、置き換えた「"B"」の直後に移動しますし、コピー元も「"B"」だけコピーしたためPositionは「"B"」の直後となります。
CopyToでの動きを整理すると、以下のようになります。
パラメータ | Copy範囲/貼付け範囲 | Positionの移動先 | |
---|---|---|---|
Copy元 | -1 | Position~末尾 | 末尾 |
文字数 | Position~指定文字数分 | Copy範囲の後ろ | |
Copy先 | -1 | Position~Copy範囲分 | 貼付け範囲の後ろ |
文字数 |
なおPositionの値はバイト単位なので、そのStreamの文字コードを把握し、並んでいる文字の1つ1つが何バイトなのか、またBOMの有無まで考慮しないと、目的の文字位置(=Position値)を設定できないことに注意して下さい。
また図58はテキスト型(Type=2)の表となっていますが、バイナリ型(Type=1)の場合はPosition値もコピーする個数もバイト単位となります。
CopyToメソッドを使ったコード例を3種類紹介します。
まず下記は、ファイルの文字コードを変換するコードです。CopyToメソッドでStream内容をコピーすると「コピー元の文字コードからコピー先の文字コードに変換」される機能を使っています。ここではShift-JISのテキストをUTF-16LEに変換します。
- '========== ⇩(16) CopyToで文字コードを変換 ============
- Private Sub test14()
- Dim ADOst_SJIS As Object
- Const FN_SJIS As String = "Test_SJIS.txt" '←Shift-JISファイル
- Dim ADOst_UTF16 As Object
- Const FN_UTF16 As String = "Test_UTF16.txt" '←UTF-16LEファイル
- Set ADOst_UTF16 = CreateObject("ADODB.Stream")
- ADOst_UTF16.type = 2
- ADOst_UTF16.Charset = "Unicode" '←UTF-16LE
- ADOst_UTF16.Open
- Set ADOst_SJIS = CreateObject("ADODB.Stream")
- With ADOst_SJIS
- .type = 2
- .Charset = "Shift-JIS" '←Shift-JIS
- .Open
- .LoadFromFile FN_SJIS
- .CopyTo ADOst_UTF16, -1
- .Close
- End With
- ADOst_UTF16.SaveToFile FN_UTF16, 2 '←UTF-16LEファイルとして出力
- ADOst_UTF16.Close
- Set ADOst_SJIS = Nothing
- Set ADOst_UTF16 = Nothing
- End Sub
448~451行目では、UTF-16LEのStreamオブジェクトをOpenしています。この「ADOst_UTF16オブジェクト(UTF-16LEのStream)」は、459行目での「データのコピー先」となります。
453~457行目では、Shift-JISのStreamオブジェクトをOpenしています。こちらはデータの送り先です。
458行目「.LoadFromFile FN_SJIS」では、Shift-JISのテキストファイルからStream内にデータを読み込んでいます。
459行目「.CopyTo ADOst_UTF16」では、Shift-JISのStreamのデータをUTF-16LEのStreamにコピーしています。Shift-JISのStreamのPositionは、ファイルを読み込んだだけですので先頭(Position=0)に居ますし、CopyToメソッドの第2パラメータを「-1」としている為、全データのコピーとなります。
コピーされたデータはUTF-16LEに変更され、且つ図12でも分かるようにBOM付です。
463行目「ADOst_UTF16.SaveToFile FN_UTF16, 2」では、「UTF-16LEのStream」のデータをファイルに書き出しています。 コピー前後のテキストファイルは以下のようになります。
図60
次は、BOM付のファイルをBOM無しのファイルに変換するコードです。対象はBOMが有りうる「UTF-8 、UTF-16LE/BE」になります。ここでは、UTF-8(BOM付)のファイルをUTF-8(BOM無し)ファイルに変換しています。
- '========== ⇩(17) BOM付ファイルをBOM無しに変換 ============
- Private Sub test15()
- Dim ADOst_Txt As Object
- Const FN_Txt As String = "Test_UTF8.txt" '←BOM付
- Dim ADOst_Bin As Object
- Const FN_Bin As String = "Test_UTF8N.txt" '←BOM無し
- Set ADOst_Bin = CreateObject("ADODB.Stream")
- ADOst_Bin.type = 1
- ADOst_Bin.Open
- Set ADOst_Txt = CreateObject("ADODB.Stream")
- With ADOst_Txt
- .type = 2
- .Charset = "UTF-8"
- .Open
- .LoadFromFile FN_Txt
- .Position = 3 '←UTF-8はBOMは3バイト(UTF-16は2バイト)
- .CopyTo ADOst_Bin, -1
- .Close
- End With
- ADOst_Bin.SaveToFile FN_Bin, 2 '←BOM無しファイルとして出力
- ADOst_Bin.Close
- Set ADOst_Txt = Nothing
- Set ADOst_Bin = Nothing
- End Sub
487~489行目で、バイナリ型としてStreamを開きます。これをオブジェクト変数「ADOst_Bin」としています。
491~495行目で、UTF-8(BOM付)のテキスト型としてStreamを開きます。こちらは「ADOst_Txt」としています。
496行目「.LoadFromFile FN_Txt」で、UTF-8(BOM付)のテキストファイルを読み込みます。
文字コードUTF-8のBOMは3バイト(0xEF BB BF:「文字コード編」参照)ですので、497行目「.Position = 3」で読み取り開始点を3バイト後ろに移動させます。この位置がCopyToの起点になります。
498行目「.CopyTo ADOst_Bin, -1」で、「BOMを除いたデータ」をバイナリ型Streamにコピーします。
バイナリ型Streamに格納されたデータはバイナリとして管理されていますが、内容は「BOMを除いたUTF-8のデータ」です。
このデータを502行目「ADOst_Bin.SaveToFile FN_Bin, 2」で、テキストファイルに書き込みます。
書き込み前後の形は下図のようになります。左が「UTF-8(BOM付)」、右が「UTF-8(BOM無)」です。テキストは全く同じですが、下側のバイナリデータを見ると「BOM(0xEF BB BF)」が削除されている事が分かります。
図62
なお、498行目「.CopyTo ADOst_Bin, -1」では「テキスト型Stream → バイナリ型Stream」へのCopyToメソッドでのコピーは出来ますが、その逆を行おうとすると「操作は許可されていない」との実行時エラーが発生します。
3つ目は、テキストファイルを結合するコードです。図59で「文字コードの変換」が出来るのと同様に、異なる文字コードファイルの結合も可能です。
下ではShift-JISのテキストの末尾にUTF-16のテキストを結合(=追加)し、Shift-JISファイルに戻しています。
- '========== ⇩(18) テキストファイルの結合 ============
- Private Sub test16()
- Dim ADOst1 As Object
- Const FN1 As String = "Test_SJIS.txt"
- Dim ADOst2 As Object
- Const FN2 As String = "Test_UTF16.txt"
- Set ADOst1 = CreateObject("ADODB.Stream")
- ADOst1.type = 2
- ADOst1.Charset = "Shift-JIS"
- ADOst1.Open
- ADOst1.LoadFromFile FN1
- ADOst1.Position = ADOst1.Size '←Positionを末尾に移動
- Set ADOst2 = CreateObject("ADODB.Stream")
- With ADOst2
- .type = 2
- .Charset = "Unicode"
- .Open
- .LoadFromFile FN2
- .CopyTo ADOst1, -1 '←末尾に追加
- .Close
- End With
- ADOst1.SaveToFile FN1, 2
- ADOst1.Close
- Set ADOst2 = Nothing
- Set ADOst1 = Nothing
- End Sub
527~530行目で、Shift-JISのテキスト型でStreamを開きます。これを「ADOst1」オブジェクトとしています。
531行目「ADOst1.LoadFromFile FN1」でShift-JISのテキストファイルを読み込みます。
読み込んだだけではPositionは先頭に居ます。今回は「末尾にテキストを追加」しますので、532行目「ADOst1.Position = ADOst1.Size」でPositionを末尾に移動させます。
534~538行目で、Unicode(=UTF-16LE(BOM付))のテキスト型でStreamを開きます。
539行目「.LoadFromFile FN2」で、UTF-16LEのテキストファイルを読み込みます。
540行目「.CopyTo ADOst1, -1」で、UTF-16LEのデータをShift-JISデータの末尾にコピーします。
「Shift-JISのデータ」+「UTF-16LEのデータ」という形になったShift-JISのStreamデータを、544行目「ADOst1.SaveToFile FN1, 2」でファイルに書き出します。
元の2つのファイルと、書き換えられたファイルの形は下図のようになります。左側が「Shift-JISの元ファイル」、中央が「UTF-16のファイル」、右側が「2つのテキストを結合したShift-JISファイル」です。
図64
図64の下側のバイナリエディタを見ると、UTF-16LEのバイトデータをそのままShift-JISの末尾に追加しているのでは無く、図59と同様に「文字コード変換」を行いながら追加しているのが分かるかと思います。
(CopyToで置き換えられる文字数) 図57等の説明では、あたかも「1文字=1バイト」のような説明図を使用しました。この図だけを見ると、例えば「Copy元の2文字をCopyToすれば、コピー先の2文字が置き換えられる」ような感覚になるかもしれません。 しかし図59や図63でも分かるように、文字は「Streamの文字コードに従って処理」されて格納されるため、ほぼ 「Copyする文字数 ≠ 置き換えられる文字数」となると考えた方が良いと思います。 この現象を確認するため、下記コードでは「"Aアあ"」という3文字を「"1234567890"」という10文字の先頭から上書きしています。文字コードは両方ともUTF-8です。
569行目「ADOst1.WriteText "1234567890"」では、コピー先のStreamにデータを格納しています。置き換えるのは先頭部分ですので570行目「ADOst1.Position = 0」で、Positionを先頭に移動させています。 577行目「.WriteText "Aアあ"」では、コピー元のStreamにデータを格納しています。こちらも全データをコピーする為に、578行目「.Position = 0」で先頭にPositionを移動させています。 579行目「.CopyTo ADOst1, -1」で、「"1234567890"」の先頭部分を「"Aアあ"」で置き換えるように、データをCopyToしています。置き換えた結果は、584行目「MsgBox ADOst1.ReadText」で表示させています。 置き換え結果は、以下の左側のように「"Aアあ890"」と6文字になります。 図66 これは、上書きされる側(ADOst1)のStreamには数字のみ(UTF-8では、数字は1バイト/文字)であるのに対し、上書きする側のStreamの「Aアあ」の内、アルファベットの「A」は1バイトですが、半角カナの「ア」はUTF-8では3バイト、全角ひらがなの「あ」も3バイトである為です。 「Aアあ」では「全7バイト」を上書きしますので、上書きされなかった文字は「10 - 7 = 3バイト」の「"890"」となる訳です。 同じ文字コードでこのような現象が発生するのはUTF-8とShift-JISだけだと思いますが、これが異なる文字コードの間で行うと、かなり計算が面倒になります。 空のStreamにCopyToをしたり、末尾にCopyToしたりする時には、文字数についてはあまり気にしないと思います。しかし「文字列を置き換えたい」ような場合には「Stream内容を一旦VBA内の文字列にし、文字列として処理後にStreamに入れ直す」方が安心だと思われます。 以下では、VBA内で文字列の置換をしてからStream処理をしています。
609行目「str1 = str2 & Mid(str1, Len(str2) + 1)」では、文字列str1の先頭部分を文字列str2で置き換えています。Len関数を使っているので「"Aアあ"」は3文字という計算です。 一方、見え消しをしている610行目「str1 = str2 & Mid(str1, LenB(StrConv(str2, vbFromUnicode)) + 1)」は、全角を2文字と数える方法で、「"Aアあ"」は4文字という計算です。 この数式で置き換えた文字列(str1)を617行目「.WriteText str1」でStreamに格納し、処理を行っています。 この方法でしたら比較的簡単で、後からコードを見た人にも分かり易いのではと思います。 |
3-11.Close
Closeメソッドは、開いているオブジェクトを閉じます。構文は以下で、パラメータはありません。Stream.Close
オブジェクトを閉じても「オブジェクト = Nothing」をしない限りメモリ上には残るため、再度開く(Openメソッドの実行)ことはできます。
3-12.Flush
Flushメソッドは、Streamのバッファの内容を全て吐き出す処理をします。構文は以下で、パラメータはありません。Stream.Flush
ファイルへのデータ書き込み中にこのメソッドを実行すると、全ての変更内容を確実に書き込むようです。但しFlushを実行しなくても更新処理は自動的に行われ、且つCloseメソッドでStreamを閉じる時にも自動的にFlushの機能が行われるので、明示的に実行しなくても良さそうです。
大量のデータを読み書きした直後の処理では、中途半端なデータ内容にならないようにFlushの実行が必要になる場合があるのかもしれませんが、Streamでは非同期処理は出来ないので、それも心配ない気がします。
3-13.Cancel
Cancelメソッドは、非同期メソッドの実行をキャンセルさせます。構文は以下で、パラメータはありません。Stream.Cancel
しかし今回のようなテキストファイルの読み書きでは、「非同期処理」の設定が出来ません。
つまり、Openメソッドの第3パラメータ(OpenOptions)に adOpenStreamAsync(値=1)が指定できるのであれば、非同期モードでStreamオブジェクトを開くことができるはずなのですが、「Openメソッド」の所でも紹介したように adOpenStreamUnspecified(値=-1)以外は設定できません(既定値なので、通常は設定しない)。
「ファイルの呼び出しを取り消す」などと説明をしているサイトもあるようです。しかし、100kB以上のテキストファイルをLoadFromFileで呼び出した直後にCancelも実行してみたのですが、同期状態ですので当然ながら「ファイルの読み込みが完了」してから次のコードに移っています。
非同期の設定が可能なADOのConnectionオブジェクト等のみで機能するメソッドのようです。
アプリ実例・関連する項目
「CSVファイルの読み込み」「CSVファイルでデータを読み書きする月間予定表」
サンプルファイル
今回、説明の中で紹介したコードは、以下のサンプルファイルの標準モジュール(Module1)に記載しています。実行するにはVBEから直接実行してください。また読み書きするテキストファイル名にはPathを指定していないので、既定のドキュメントフォルダー
「C:¥Users
フォルダーにファイルが無い場合にLoadFromFileを実行するとエラーとなりますので、事前にテキストファイルを作成してから試してください。その場合、ファイル名と併せて文字コードも合わせる必要があります。
テキストファイルの読み書き_ADODB.Stream編(its-050.xlsm)
セキュリティ向上を目的として「インターネット経由でダウンロードしたOfficeファイル(Excel等)のマクロは、既定でブロック」されるようにOfficeアプリケーションの既定動作が変更になりました。(2022年4月より切替開始) 解除の方法については「ダウンロードファイルのブロック解除方法」を参照下さい。 |