テキストデータを開く(書き込み用)

テキストデータをメモリ上に開くにはOpenステートメントを使用します。その時に書き込み用で開くコードと注意点

'書き込み用で開く(既存は保持される)
Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Append As #1
'書き込み用で開く(既存は消去される)
Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Output As #1

テキストファイルを開くには「Open」ステートメントを使用します
このステートメントを実行する事でメモリ上にテキストデータを展開します

ステートメントの書式

引数(太字は必須引数)
Open Pathname For Mode As # Filenumber

引数「Pathname」・「Filenumber」に関しては以下の記事で解説を行っていますので確認してください

「Mode」は開くデータの扱いに関しての指定方法です
書き込み用に開く場合は、「Append」・「Output」のどちらかを指定します

「Append」・「Output」の違いについて

どちらのキーワードを指定しても、テキストデータの書き込みが可能です
違いは既存のデータの扱いになります

Appendは既存のデータに追記していきます
Outputは既存のデータを消去して追記していきます

実際の動きをテキストデータで確認してみます

処理前のテキストデータ
処理実行前のテキストデータ

まずは、処理を実行する前のテキストデータをメモ帳で開いた状態です
この文字列にそれぞれのモードで文字列の追記を行います

Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Append As #1

Print #1, "新しい追加行,白"

Close #1

このコードでは、Appendモードでの追記を行います
追記にはPrint#ステートメントを使用します

Appendでの書き込み時のテキストデータ
Appendでの追記実行時

コード実行後のテキストデータです
もともとある3行の下にコードで追記した文字列が挿入されています

Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Output As #1

Print #1, "新しい追加行,銀"

Close #1

こちらでは、Outputモードでの追記を行います

Outputでの書き込み時のテキストデータ
Outputでの追記実行時

コード実行後のテキストデータです
こちらでは、もともとある3行のデータは消去され、コードでの追記文字列が挿入されます

新規ファイルにデータを書き込む際には、どちらのモードを使用しても構いませんが基本的には、新規時はOutput、既存に追記時はAppendをそれぞれ使用するようにします

既存テキストデータの文字形式に注意

文字化けした状態
文字化けした状態

なお、書き込み時に文字形式がANSI形式に保存されます
もともとUTFなどで保存されていた場合、Appendモードで既存の文字列の文字形式が違ってしまい、文字化けが発生します

テキストデータを開く(読み込み用)

テキストデータをメモリ上に開くにはOpenステートメントを使用します。その時に読み込み用で開くコード

'読み込み用で開く
Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Input As #1

テキストファイルやCSSデータをテキスト形式で開くには「Open」ステートメントを使用します。

このステートメントはあくまでもテキストファイルを開く動作を行うものです
この開いたデータに対して各メソッドを使用して処理を行っていきます

ステートメントの書式

引数(太字は必須引数)
Open Pathname For Mode As # Filenumber

「Pathname」はテキストデータとして開くファイルの絶対パスを指定します
単体のファイルを指定する必要がありますので、ここでワイルドカードでのファイル名指定はできません
その場合は、FileSystemObjectを使用します

また、ここで指定したファイル名のデータが存在しない場合はファイルが新規作成されます
ただ、このOpenステートメント時点では作成はメモリ上で作成されているだけです
Closeステートメントを実行する事で保存され、ファイルが作成されます

「Mode」は開くデータの扱いに関しての指定方法です
読み込み用で開く場合には「Input」を指定します

「Filenumber」はこのテキストデータの内部的なファイル番号です
1~511の整数値で番号を指定します
この番号は同時に開く場合に、重複しない番号にする必要があります
重複した番号を指定するとエラーが発生します

同時に開くことがなく、単一で処理を実行する場合は「1」を固定で使用してかまいません

この「同時に開く」というのは、OpenステートメントからCloseステートメントの間のことで、処理実行中という意味ではありません
つまり、別のファイルであっても一度Closeステートメントで閉じた後であれば番号を振り替える必要はありません

この番号は未使用番号を取得する関数がありますので、そちらに関しては以下の記事を確認してください

テキストデータを扱う

テキストデータを扱う際の操作の流れの解説と記事一覧

VBAでテキストデータを扱うには、まずテキストデータを開く必要があります
この開くという動作は、ブックを開くというアプリを起動してデータを開く方法ではなくメモリ上にデータを開きます

以降、このメモリ上でテキストデータは操作します
追記や編集、取得を行う操作を行った後、最後にテキストデータを閉じる操作を行います

テキストデータとは、文字列のみで表現されたデータのことになります
主な使用場面は、通常のテキストデータやCSVデータ、設定用にiniファイルを操作することもできます

テキストデータを扱う際の流れは以下の通りになります

  1. テキストデータを開く-Openステートメントを使用してデータを開く
  2. テキストデータの各種操作を実行する-編集・取得を行う
  3. テキストデータを閉じる

以上の操作を行い、テキストデータの形式や使用用途によって操作を分けて実行します

テキストデータを扱う際、特にcsvデータはデータサイズが特大になりがちです
このデータを操作する際には、メモリ上に展開して操作することから処理時間が異様にかかってしまうことがあります
特にループ処理時に発生しやすいので、ループ処理に組み込む際は実装前に想定以上のビッグデータでテストしてみたほうが良いです

また、テキストデータ操作はFileSystemObjectでも行うことができます
こちらは、単一のテキストファイルだけでなくフォルダ内にあるすべてのファイルを操作する時などでは合わせて実装できるので、便利ですし
コードも分かり易くて使いやすいです

ただ、本記事の操作方法は標準コードなので前準備が必要ないのが利点です

単体のテキストデータでは、本記事操作を行う
複数のテキストデータでは、FileSystemObjectを使用する

この様に使い分けると良いと思います

Endステートメントの有効利用

Endステートメントは処理を強制終了させます。数少ない有効利用場面の解説

'全ての処理を強制終了
End

処理を強制的に終了させるには「End」ステートメントを使用します

このステートメントの使用上の注意点があります、以下の記事で解説しています

基本的にこのコードは強制終了という側面から、あまり多用すべきコードではありません

終了する場合はしっかりプロシージャの最後まで実行するべきです

ですが、それも状況次第で有効に利用できる場合があります

フォームの起動を強制終了

ユーザーフォームを起動させるには、ShowメソッドとLoadステートメントがあります

どちらも、必ずInitializeイベントを発生させます
なのでフォームの初期化処理はこのイベントに作成します

起動条件を設定したフォームの場合は注意する点があります

例えば、入力補助のフォームであれば
作業するシート以外では、フォームは表示される必要はありませんし、場合によってはバグが発生する可能性があります
そもそも使用しないシートでフォームが表示されるのも邪魔です

そんな場合には、フォームの起動時に条件分岐を行い条件に一致しない場合にはフォームを起動しないようにします

そして、通常は問題にならないこの状況が問題になるのが、初期化処理に時間が多少かかる場合です

条件分岐によりフォームを表示させないのに、待ち時間をユーザーに発生させてしまいます
シートを切り替えるたびに、数秒待たされるような状況になったら非常に迷惑です

解決法として、あるのは2通りです
読み込みコード実行前に条件分岐を行うか、Initializeイベントの処理最上部に条件分岐を行うかのどちらかです

If ActiveSheet.Name = "対象" Then
UserForm1.Show
Else: End If

読み込みコード実行前に行うのが通常の方法です
If分岐で条件に一致しなければ、読み込みコードは実行しないようにします

ただ、このコードでも問題はあります
基本的にこういった処理の場合は、シートイベントのActivateイベントで表示処理、Deactivateイベントでフォームを終了させる動きにします

こうしておかないと、表示は条件分岐で対象シートで行えても、シートを切り替えた時にフォームが表示されたままになるためです

そこで問題の発生です

対象シートが複数あった場合、全てのシートに条件分岐を作成する必要があります
少しの修正があっても大変になってしまいます

そこで、フォームのInitializeイベントで一括して条件分岐を行います

If Not ActiveSheet.Name = "対象1" Or ActiveSheet.Name = "対象2" Then
End
Else: End If

Initializeイベントの最上部にこのコードを挿入します
アクティブシートの名前が指定シートでなければ、Endステートメントにより強制終了します
指定シートであれば処理が継続して、多少時間のかかる初期化処理が実行されます

Initializeイベント中に、Unloadステートメントを実行するとエラーが発生しますので、このEndステートメントが効果を発揮します

当然ですが、呼び出し元処理(ShowメソッドやLoadステートメント)も強制終了しますので読み込み処理には他の処理は入れないようにしましょう

使用場面は限られて少ないですが、あると便利な場面もありました

なお、気づいた方もいるかもしれませんが、終了処理に対応してません
これにはイベント最強のWithEventsキーワードを使用します

レジストリのデータを削除

レジストリに保存したデータを削除するコード(DeleteSettingステートメント)

'レジストリの値の削除
DeleteSetting "ファイル名", "モジュール名", "データ名"
'レジストリのsection以下削除
DeleteSetting "ファイル名", "モジュール名"
'レジストリのappname以下削除
DeleteSetting "ファイル名"

レジストリに保存したデータを削除するには「DeleteSetting」ステートメントを使用します

ステートメントの書式

引数(太字は必須引数)
DeleteSetting appname, section, key

引数の指定によって削除される階層が変化します
引数と階層は以下の記事で解説しています

VBAのリファレンスでは、2つ目の引数「section」は必須となっていますが、実際には省略しても実行されます

削除する範囲と必要性

指定した箇所以下が全て削除されます

DeleteSetting "ファイル名", "モジュール名"

つまり、この様にsectionまでの指定を行うとsectionが削除されます
なので値のキーは全て削除されます

DeleteSetting "ファイル名"

appnameのみ指定した場合は、全てのデータを削除します

以下の全データとは言っても、VBAで作成したデータなので、知らないデータや別アプリのデータが削除されるわけではありませんので安心してください

また、このキーより上位のアイテムは本当に必要が無ければ削除したほうが良いと思います

これはエクスプローラーのフォルダ階層と同じ構造です
つまり、フォルダ内にあるファイルを全て削除しても空フォルダがそこに残ったままの状態になっています
空なので、影響はありませんが無駄ファイルであることは間違いありません
キーの値が全て必要ないのであれば、appnameのみで指定して全て削除してしまっていいと思います

レジストリのデータを取得

レジストリのデータを取得するコード(GetSetting関数)

'レジストリの取得
Debug.Print GetSetting("ファイル名", "モジュール名", "データ名", 0)

レジストリのデータを取得するには「GetSetting」関数を使用します

関数の書式

引数(太字は必須引数)
GetSetting(appname, section, key, default)

引数「appname」「section」「key」は、データまでのパス指定です
このあたりの解説は以下の記事で行っています

引数「default」は、パスも含めて指定のデータが存在しなかったときに返される値になります
「appname」は存在しているが、「section」が存在していなかった場合もこの引数の値が返される訳です

省略可能で、省略時は「””」(空白文字列)が返されます
データが無くてもエラーが発生しない点には注意が必要かもしれません

レジストリのデータ取得に関して

レジストリにデータを保存する場合は、SaveSettingステートメントでした

こちらは関数です、値の取得を行うため戻り値が存在するためです
なので、こちらのコードでは引数を「()」で囲むのと、代入先を指定する事を忘れないようにしましょう
例コードではイミディエイトに出力しています

また、レジストリの操作は編集・削除は要注意ですが
取得を行うこの関数の使用に関してはそこまで気にしなくても大丈夫です
取得を行うだけで編集は行いません

ただ、見たらあかんやつを見たらあかんので規則は大切に

レジストリにデータを保存

レジストリにデータを作成・保存するコード(SaveSettingステートメント)

'レジストリの保存
SaveSetting "ファイル名", "モジュール名", "データ名", "データ内容"

レジストリにデータを作成・保存するには「SaveSetting」ステートメントを使用します

ステートメントの書式

引数(太字は必須引数)
SaveSetting appname, section, key, setting

引数は全て必須項目となっており、引数「setting」が保存するデータとなります
それまでの3つの引数はデータの名称設定となり、階層構造になっています
全て文字列で指定し、決まった名称もありませんので自由に指定できます

レジストリの階層図
レジストリの階層図

レジストリの各引数の階層図になります
こういった形で階層が引数の順番に並びます

最上位の「VBA専用」のフォルダより上位は編集できません
編集可能な部分が限定的なので、他のVBAで作成されていなければ何もデータは存在しませんので好きに扱っても問題ありません

存在するデータを指定した場合は、既存のデータが上書きされ、データが無ければ新規作成されます

レジストリ使用上の注意点

レジストリには各アプリの重要なデータも含まれています
いくらVBAでは触れないとはいっても、レジストリの編集をしているという事実は変わらないので、基本的に編集する場合は要注意です

また「レジストリを編集していいすか?」と、気軽に確認するのはやめましょう
レジストリを知っている人からすれば、そんな恐ろしいこと許可できひんと言われます

とはいえ、上記にもあるようにVBAのコードで編集する分には問題ありません
元々何もデータが入っていない場所を編集するのでだいじょうぶです
他の場所を編集しようとしてもVBAのコードでは実行できません

ただ、レジストリエディタは簡単に起動が可能で編集もそのエディタで行えます
ここでは触れませんが、Web検索すればすぐに起動方法はでてきます
ですがVBAでの使用範囲内であれば使用しないでください
何かあったときの責任は非常に大きい可能性がありますので

VBAで触れる範囲のデータは全てVBAで取得も作成も削除も行えます
ま、しなくていいことはしないほうがいいですよね

Application.Volatileについて

Application.Volatileメソッドはユーザー定義関数を揮発性にする。揮発性の動きの解説

'揮発性にする
Application.Volatile

「Application.Volatile」メソッドは、ユーザー定義関数を揮発性にするコードです

揮発性とは、引数に関係の無いセルが更新された時でも再計算が実行される状態になることを言います
通常は、引数に関係あるセルが更新された時に再計算は実行されます

揮発性ではない通常の再計算

ユーザー定義関数のコード画像
ユーザー定義関数のコード

まずは、画像のようなユーザー定義関数を作成したとします
この関数は、計算時に現在日時を文字列付きで返す関数になります

ワークシートに関数を入力したときの動き
ワークシートに反映した時

この関数をワークシートのセルに入力します
すると、その日時と文字列が結合されてセルに表示されます
ここでは「最終計算日時: 2020/01/09 21:59:55」が返されています
実際に関数を確定させて計算させた時間です

再計算されるときの動き
再計算時の動き

次に、この関数の関係するセルは引数には指定は無いので自分のセルのみです
なので、一度入力モードにしてそのまま確定させます
すると、再計算が実行されて「最終計算日時: 2020/01/09 22:03:14」が返されます

しかし、A2を更新した時点では更新されません
関係するセルではないためです

このままでは、この関数は本来シートのデータを更新させた履歴を表示する関数という役割を果たさずに、ただこの関数を入力した時間を表示するだけの関数になってしまい何の意味もない関数になります

メソッドを使用した揮発性の再計算

揮発性にしたユーザー定義関数のコード画像
メソッドの挿入

そこで、このメソッドを挿入します
このメソッド自体はどこに入れても構いませんが、揮発性にしていることを分かり易くするために、最上部に挿入してください
これでこのユーザー定義関数は揮発性になったため、関係のないセルが更新されても再計算が実行されます

揮発性になった関数の再計算の動き
揮発性になったユーザー定義関数

今回の動きでは、A2の更新をしただけで「最終計算日時: 2020/01/09 22:58:10」が返されています

このメソッドでは、同じブック内のセルが更新された時に再計算されるようになるため、このユーザー定義関数の文字列にユーザー名などを追加すれば
誰がいつ最後に更新したかが分かる関数になります

こういった、関係ないセルを更新した時でも再計算を実行させたい場合に使用するメソッドです

揮発性にすることの注意点

このメソッドを使用すると、再計算の回数が激増します
なので再計算にかかる時間が大幅に増える可能性があります

また、関係の無いセルを更新するだけで再計算するということはセル参照するような処理にしていた場合は予期しないセルの動きによるバグの可能性もあります

このメソッドは、とりあえず使うような代物ではなく
明確に処理の流れを理解した上で必要なら使用するものです

ユーザー定義関数にハマって、すぐに使ってしまう愚を犯さないようにしてください、誰かさんはこれを入れてエラー無視までいれて強制的に処理を走らせていましたが、今考えるとすごい話です

なお、こんな処理ならChangeイベントの方が向いていると思いますが、目的に対する手段がいろいろあるのもVBAの楽しいとこですね

エラー処理(エラー処理を解除させる)

On Error GoTo 0ステートメントはエラー処理を初期化することにより、それまでにあるエラー処理を解除します

'エラー処理を解除する
On Error GoTo 0

「On Error GoTo 0」ステートメントは、エラー処理を初期化します

「On Error GoTo」ステートメントや「On Error Resume Next」ステートメントで設定したエラー処理を解除する際に使用します

エラー処理初期化したときの動き
エラー処理を初期化したときの動き

画像の動きを確認してください

「Err.Raise(1)」は1番の実行時エラーを発生させるコードです
1つ目では、エラー発生を無視して継続するようになっているためエラーメッセージは発生せず、処理が継続しています

2つ目では、On Error GoTo 0ステートメントによりエラー処理が初期化されているため通常通りにエラーメッセージが表示され処理が停止します

エラー処理を作成した場合は必須

エラー処理の設定を行った場合には必ず使用してください

このコードを使用しないと、後続処理全てで同じエラー処理が適用されるので意図しない動きが発生する可能性があります

またこのコードを実行時にエラー情報はクリアされます
エラー情報自体は、発生時に保持されています

エラー処理(無視して継続)

On Error Resume Nextステートメントを使用することでエラー発生による処理停止を防ぐことが出来ます

'後続のエラーを無視する
On Error Resume Next

実行時に発生するエラーを無視して処理を継続するには「On Error Resume Next」ステートメントを使用します

このコード以降に発生するエラーで処理の停止が無くなり、処理が継続されるようになります
エラー発生が意図したもので、予想の範囲内である場合に使用します
なお、エラーが発生しなくなるわけではありません

フォルダの作成は、すでに存在する名前を指定するとエラーになりますがそのエラーは無視することで、フォルダの作成が出来るときはする、出来ない時はしない、という動きにすることにできます

また、オブジェクト取得の際に存在しないオブジェクトを取得しようとするとエラーが発生しますが、そのオブジェクトの生成を待ったり、判定に使用したりする際にエラーを無視させます

エラーの発生は基本的には必要な情報の提示になります
これを抑止する事で、予想外のエラーが発生しても気づかないこともありますので多用すべきコードではありません

また、このコードは以降の全ての処理に適用されるので無視するべき処理が終わったらすぐに「On Error GoTo 0」ステートメントを使用してこの無視する設定を解除してください