処理を指定行に移動する

GoToステートメントは指定の行から処理を実行できます

'指定箇所に処理を移動する
GoTo 移動

移動:

処理の途中で条件分岐などにより、指定処理を飛ばして後続処理を実行したい場合があります
その時に使用するのが「GoTo」ステートメントを使用します

このステートメントの後にラベル名を指定することで、そのラベル行に処理が移動します

ラベルに関しては「ラベル名」に「:」を結合して指定します
日本語も使用できますので、わかりやすい名前を付けます

処理の実行が移動している状態の動き
コードの動き

実際の動きの確認です
GoToステートメントの時点でラベル行に処理が移動するので、後続の「MsgBox “”」が実行されていません

また、ラベル行は処理ではありませんので処理実行の黄色にはなりません
ラベル行の次の行から処理が実行されます

ちなみに、行ラベルはタブを挿入できません

このステートメントは一見、便利なコードのように感じるかもしれませんが多用は禁物です

中でも、一番注意してほしいのがループ処理に使用する場面です
ループの途中で強制的にループを抜けさせる場合、配列データがロックしたりする場合があります
ループでそういった移動処理はなるべく使用しないような処理に作成出来るように検討してください

また、あまりにも多く使用するとどこからどこに飛ぶか分かりにくくなる可能性があります
ステートメント自体の使用数は多くても構いませんが、ラベル行は限定するようにします

自分がよく使用する場面は、処理途中で条件が整っていないために処理を終了する際に、その判定までに複数の変数を使用していて全てを解放する必要がある場合に、Exitステートメント実行前にそれら全てを解放するのではなく、通常処理での解放の手前にGoToステートメントで移動させて、解放を1か所だけにする
というような使い方をします

ちなみに、このステートメントは行ラベルの位置に移動するものなので、下方向だけでなく上方向に移動させることも出来ます
条件が整ってなければ、再度上に戻って処理を実行するようなことも可能です
ただ無限ループに注意が必要です

引数を渡してプロシージャを実行する

引数付きのプロシージャの作成方法と呼び出し方法について

'セルを引数に持つプロシージャ
Sub 呼出テスト(引数 As Range)

End Sub

プロシージャの呼び出しにはCallステートメントを使用します
この時に引数付きのプロシージャを実行する事ができます

その際には呼び出し先のプロシージャで引数の設定を行う必要があります

関数と同様にプロシージャ名の隣の「()」の中に指定を行います
通常引数の無い場合は、空白状態になっています

ここは引数を設定するための場所になります

引数は、変数と同じものを指定します
書き方も変数の宣言と同じものになります

実際の処理の動き

Sub 処理テスト()

 Call 呼出テスト(Range("A1"))

 Call 呼出テスト(Range("A1:A10"))

End Sub

Sub 呼出テスト(引数 As Range)

Debug.Print 引数.Address

End Sub

処理の動きの確認には以上のコードを使用します

引数付きの処理を呼び出したときの動き
引数付きの処理の呼び出し

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

 Call 呼出テスト(Range("A1"))

 Call 呼出テスト(Range("A1:A10"))

Callステートメントで2回処理を呼び出しています
その際に、引数としてセルを指定しています

指定方法はプロシージャ名の後に「()」を指定して、中に引数に設定するものを記載します

Sub 呼出テスト(引数 As Range)

Debug.Print 引数.Address

End Sub

呼び出し先の処理では、引数は「(引数 As Range)」として指定されています

ここに呼び出し時に指定するセルを代入することができます
代入されたものは通常の変数と同じ使い方になるので、Addressプロパティでセルアドレスを取得することが出来ます

Callステートメントにより同様の処理をまとめることが出来ますが、こうして対象が違っていてまとめることが出来ない場合に使用できます

なお、この引数付きのプロシージャからデバッグモードに入ることはできません
なので動作確認を行いたい場合は、呼び出し元のプロシージャから実行することでデバッグモードを行うことが可能になります

また、ここではSubプロシージャで行っていますが、Functionプロシージャでも全く同様に使用できます

エラー処理(エラー内容をクリアする)

On Error GoTo -1ステートメントは、エラー情報をクリアすることが出来ます。再度エラー処理を適応することが可能になります

'エラー情報をクリアする
On Error GoTo -1

処理を移動させずに、エラー状態の解除のみを実行するには「On Error GoTo -1」ステートメントを使用します

このステートメントを使用する場面は、エラーが発生している状態で、さらにエラーが発生した場合にどうしたいかによります
つまりエラー処理中のエラーはどうするか、という点です

エラー処理中のエラー発生について

エラールーチン中にエラーの再発生したときの動き
エラー処理中のエラー発生時の動き

画像の動きを確認してください
エラーが発生した際、On Error GoToステートメントにより「エラー発生」のラベル行に処理が移動します

このとき、エラー情報は発生したものが保持された状態です
この状態をエラールーチンが行われている状態といいます

このエラールーチン中に、再度エラーが発生した場合はステートメントの影響に関係なく実行時エラーが発生して処理が停止します

エラー処理とはいえ、処理である以上その処理にエラーが発生する可能性があります
その際にユーザーに処理停止を対処させるのは難しい話である場合が多いと思います

そこで、エラー処理中にエラーが発生したら、エラー処理を行いたい
こういった状況が出てくることがあります
そんなときに使用するのが、このステートメントになります

ステートメント使用時のエラー処理

エラールーチン中にエラーを解除する動き
エラー情報をクリアした時の動き

On Error GoTo -1ステートメントを使用している場合の動きになります

エラー発生:
On Error GoTo 0
On Error Resume Next
'Err.Raise (1)
On Error GoTo -1
Err.Raise (1)
Debug.Print "エラー終了"

この処理が画像のエラー処理部分になります

On Error GoTo 0

まず、最初にOn Error GoTo エラー発生の設定を解除するためにこのコードを実行します
これをしていないと、後で発生するエラーでまた「エラー発生:」に戻ってきてしまい無限ループに入ってしまいます

これで、エラーが発生したらメッセージが出て処理が停止する通常の状態になっています

ただ、このステートメントはエラー発生時の制御を解除しただけであり
エラー情報をクリアしたわけではないので、エラールーチンは継続中です

On Error Resume Next
'Err.Raise (1)

次に、エラー処理中にエラーが発生した場合の対応を作成します

今回はエラー処理中に発生したエラーは無視するように設定しますので、On Error Resume Nextステートメントを使用します

2行目はエラー発生させるコードをコメントアウトしています

実際、ここでもまだエラールーチン中なのでエラーが発生した場合は実行時エラーが発生し処理が停止します

On Error GoTo -1

ここでようやく記事のステートメントを使用しています

この時点で、エラー情報がクリアされます
そして、その時点でエラールーチンも終了しています

これにより、上記で設定したエラー発生時の制御が有効な状態になります

Err.Raise (1)
Debug.Print "エラー終了"

画像の動きと合わせて確認してください

ここでエラーを発生させていますが、処理は停止せずに最後のエラー終了の文字列出力まで実行されています

エラー処理中のエラー対応について

通常エラーの解除にはResumeかResume Nextのどちらかのステートメントを使用しますが、このステートメントでは処理が戻ってしまいます

通常、処理の組み方としてはそれが正統な作り方だと思います
でも前述したように、エラー処理を作成するということはそこでもエラーが発生する可能性は十分にあり得ます

例えば、以下の記事の動きを確認してもらいたいのですが
エラー処理でLong型変数に加算していき使用できる整数値で処理が正常に戻ります

ただ、このエラー処理でも使用できる数値がLong型の範囲内になかった場合
加算され続けて、いずれオーバーフローして実行時エラーが発生します

処理上、ほぼあり得ませんが
エラーが出ない処理は無い、ということは事実として存在します

なので、このコードを実行することによってエラーの発生がクリアされるので再度エラーが発生した場合は改めてエラー処理を適応することが出来ます

ただ、エラーの情報がクリアされるので当然エラーの発生した行もどこにあるかが分からなくなります
なので、Resumeステートメントなどでエラー発生した行に戻ることはできなくなるので注意が必要です

エラー処理(エラー処理後に戻す)

Resume Next・Resumeステートメントを使用すると、エラー処理後に処理を戻すことが出来ます

'エラー発生の次行に戻す
Resume Next
'エラー発生行に戻す
Resume

エラーが発生して、On Error GoToステートメントで処理を移動させていた状態から元のエラー発生した場所に戻すには「Resume Next」「Resume」ステートメントを使用します

戻す場所によって、コードを使い分けます

エラーが発生した行の次行に戻すにはResume Nextステートメントを使用します
エラーが発生した行に戻すにはResumeステートメントを使用します

動きの確認

エラー処理後に次の行から再実行する動き
Resume Next

エラーが発生した次の行に処理を戻すコードです

次の行に処理が戻るので、もともとエラーが発生した処理は実行されません
つまり、そのエラーが発生した処理が以降の処理に影響を与えない場合にのみ使用できます

当然、以降の処理にこのエラーが発生した処理を前提としていた場合バグが発生します

例えば、フォルダの新規作成や、存在しなかったファイルの削除など
エラーが発生すること自体は処理に影響を与えない場合に利用できます

On Error Resume Nextステートメントでもエラーが無視できますが、エラー処理が行えません
ユーザーにエラーが発生していることはメッセージで伝える必要がある場合はこちらのコードでエラー処理を作成してコードは次の行から再開させます

エラー処理後エラー発生行から実行する動き
Resume

このコードはエラーの発生した行に戻します

エラーが発生した行に処理を戻すことになるので、このエラー原因を解消するようにエラー処理を組まないと、無限ループに入ってしまいます

画像の動きを確認してください
A1セルからマイナス方向への移動ができないため、エラーが発生します

エラー処理では、移動値に数値を加算してから処理を発生行に戻しています

しかし加算値が「1」なので「-2」からは2回実行する必要があるので、2回エラーが発生して、最後にまた「0」になりA1セルが指定されて処理が正常動作に戻ります

このように、エラー処理で複数回繰り返してでもエラー原因を解消するような組み方が重要になります

ファイルの名前を付ける際に、重複しない連番をここで更新していくことで取得することが出来ます

なお、どちらのステートメントもエラー処理に使用するコードとなります
エラーが発生していない状態で実行されるとエラーが発生します

エラー発生後のエラー処理の最後に入れるようにします

エラー処理(発生時に移動させる)

エラーの発生時に任意の箇所に処理を移動させるコード。On Error GoToステートメントを使用します

'エラー発生したときは移動させる
On Error GoTo エラー発生

エラー発生:

エラー処理として、エラーが発生した際に特定の処理を実行する場面があります

その場合、エラーが発生した時点で実行するステートメント行を移動させることになります
その際に使用するのが「On Error GoTo」ステートメントです

On Error GoTo エラー発生

このステートメントに指定した文字列がラベル文字列となります
行指定も出来ますが、通常は例のように文字列で指定します
ここでは「エラー発生」の部分が当てはまります

このコード以降にエラーが発生した時点で、処理は停止せず指定した行に処理が移動されます

なお、ラベル文字列の設定はGotoステートメントと同じ設定の仕方になるので混同しないようにわかりやすい名前を付けましょう
Gotoメソッドと同じようにラベル文字列行の前には処理を終了させる「Exit Sub」を配置するようにしましょう

この設定を解除するには「On Error GoTo 0」ステートメントを使用します
解除しないと、以降の全ての処理でエラー処理が実行されますので、エラーの予期している範囲で使用するようにします

コードの動き

エラー発生時に処理を移動させる動き
エラー発生時の動き
Err.Raise (1)

このコードはエラーを意図的に発生させるコードです

このコードが実行された時点で、エラーが発生しOn Error GoToステートメントにより処理が移動しています

通常、エラー処理は正常処理の後に作成します
そのままEnd Subまで走らせる形で処理を継続させます

エラーが発生せずに処理が通常終了する動き
エラーが発生しない場合の動き

エラーが発生せずに正常に処理が実行された場合の動きです
終了コードにより、End Subまで行かずに処理を終了させています

これが通常のエラー処理の組み込み方です

処理終了コードを入れていない場合の間違った動き
終了コードを入れてない場合の動き

この画像のように、終了コードを入れ忘れていた場合、正常処理後にエラー処理が実行されてしまいます

Exit Sub
エラー発生:

なので基本的には、以上のような形でコードを作成するようにしましょう

テキストデータにデータを書き込む(Write#)

テキストデータにWrite#ステートメントで書き込みを行います。csvなどのデータとして扱う場合に使用

'テキストデータに書き込み
Write #1, "文字列", 123

Openステートメントで開いたテキストデータに書き込みを行うには「Write#」ステートメントを使用します

このステートメントはcsvデータなどの、後でデータとして扱う場合に使用します
なので、データを1行にまとめて指定する必要があります
その場合に「,」もしくは「;」を使用することでデータを結合して指定できます

引数1つ目はOpenステートメントで開いたときのファイル番号です
引数2つ目が書き込みをするデータになります

データとして書き込みをされるため、文字列の場合には「”」を含めて書き込みされます
数値は文字列ではないため、数値だけが書き込みされます

書き込みの動き

Write#ステートメントでテキストデータを書き込む場合は、csvのようなデータ群である場合が多いため、空白のテキストデータに書き込みを行います
そのため、Openステートメントで「Output」モードで既存のデータはクリアしてからデータの書き込みを行います

Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Output As #1
Write #1, "新しい追加行,白", 123
Write #1, "新しい", "追加行"; "黒", "123"
Close #1

このコードは、既存のデータがあった場合はクリアしてから2行のテキストデータを書き込みます

データの書き込みを実行した状態
実行結果

画像のテキストデータが実行結果のデータです

文字列で指定した部分は、「”」が付与されたまま書き込まれています
数値指定で書き込んだ場合はそのまま書き込みが行われます

また、「,」でも「;」でも実際に書き込まれているのは「,」になっています

空白行の追加

Write#ステートメントでは、文字列には「”」が付与されます
そのためPrint#ステートメントのように「””」と指定するとうまくいきません
何も入力しないことで空白行の指定が可能です

Write #1, "新しい追加行,白", 123
Write #1,
Write #1, "新しい", "追加行"; "黒", "123"

2行目に空白行を挿入する場合には、このコードのように2つ目の引数には何も入力しないようにします

空白がうまく挿入された状態
空白がうまく入っている状態

実行結果の画像を確認してもらうと分かりますが、2行目には何も入力されておらず、ちゃんと空白行が挿入されています

Write #1, "新しい追加行,白", 123
Write #1, ""
Write #1, "新しい", "追加行"; "黒", "123"

このコードのような状態で実行するとうまくいきません

空白がうまく入っていない状態
空白がうまく入っていない状態

画像の様に2行目が空白行とならず、「””」が入ってしまいます
空白行を挿入する場合は上記のコードのようにしてください

また、このWrite#ステートメントで書き込みをしたデータの読み込みにはInput#ステートメントを使用してください

テキストデータにデータを書き込む(Print#)

テキストデータに追記するPrint#ステートメントについて。1行の文字列を追記する場合に使用

'テキストデータの追記
Print #1, "文字列"

Openステートメントで開いたテキストデータに追記するには「Print#」ステートメントを使用します

Print#ステートメントは、csvデータのようなテキストデータはなく、文章として表示させる場合に使用します

Print#ステートメントは、コードに記載した文字列をテキストデータに書き込むので操作履歴や処理履歴などに使用することが出来ます
また、iniファイルなどの設定データをExcel外部にデータ保存する際にも使用できます

引数1つ目の「#1」はOpenステートメントで開いたときのファイル番号です
引数2つ目に追記する文字列を指定します、文字列なので「”」で囲みます

文字列の末尾には自動的に改行文字が追加されますので、ステートメントの実行前に改行文字を挿入する必要はありません

追記の動き

追記の動きの確認をするうえで重要なのが、Openステートメントでのモード選択なります
ここの設定で、既存の文字列を消去するか、それに追記していくかを選択します
ここでは、追記モードの「Append」の設定で行います

Print#ステートメント自体が文章入力の意味合いが多いので、基本的には追記モードをよく使用します

追記をするテキスト元データ
元となるテキストデータ

この画像のテキストデータに追記を行います
なお、上記にあるようにPrint#ステートメントの末尾には改行文字が付与されますが、前には挿入されません
画像を確認してもらうと分かるように、4行目に空白行が存在している状態です
この状態を前提としています

Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Append As #1
Print #1, "新しい追加行,白"
Print #1, "新しい追加行,黒"
Close #1

このコードを実行します
追記モードで開いて2行追記する処理です

追記が実行された状態のテキストデータ
コード実行結果

コードを実行すると、2行が追記されていることが確認できます
また、6行目に空白行ができていることからも改行文字がちゃんと付与されていることも確認できます

Print #1, ""

また、このコードのように追記する文字列を空白に指定することで空白行を挿入することも可能です

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

このコードのようにデータを「,」で区切って複数データを一括で指定できます
ただ、Print#ステートメントの場合はうまくスペースが入らないため、基本的には「”」で囲んだ1つの文字列データを指定していきます

「,」を使って追記したときの実行結果
「,」で区切って指定した結果

コードを実行すると、この画像の様にスペースが入ってしまいます
非常に見にくくなることこの上ないので、「,」で区切るcsvデータにはWrite#ステートメントを使用してください

このPrint#ステートメントで書き込まれたデータは、LineInput#ステートメントを使用して読み込みを行います

テキストデータからデータを取得(LineInput#)

テキストデータを見た目通りに取得を行うにはLineInput#ステートメントを使用します

'テキストデータを取得
Line Input #1, 文字列変数

Openステートメントで開いたテキストデータからデータを取得するには「LineInput#」ステートメントを使用します
文章のような形でのテキストデータや、Print#ステートメントで追記したテキストデータの取得に使用します

このステートメントは改行文字までを1つのデータとして、引数の変数「文字列変数」に代入されます

改行文字とは「Cr」のこと「Lf」では分割されないため注意が必要です
なのでWindows標準の「CrLf」は分割として有効です

取得の動き

取得するテキストデータ
取得を行うテキストデータ

この画像のテキストデータをLineInput#ステートメントで文字列を取得します

Sub 処理テスト()
    Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Input As #1
    Do Until EOF(1) = True
        Line Input #1, 文字列変数
        Debug.Print 文字列変数
    Loop
    Close #1
End Sub

このコードを実行する事で取得を行います

LineInput#ステートメントでイミディエイトに出力した結果
実行結果

コードを実行した結果のイミディエイト画面の文字列です

もともとのテキストデータと見比べてみてもらうと分かるように、そのまま全てが取得されています

csvデータ特有の「,」であったり、「”」で囲む囲まないにかかわらず記号として取得しています
取得数も3行なので、3要素になっています

この動きがもう1つの取得ステートメントであるInput#ステートメントとは全く違う点です

見た目通りに取得されるので、文章として作成されているようなテキストデータの取得時に向いているステートメントです

テキストデータからデータを取得(Input#)

テキストデータからデータを取得するInput#ステートメントの使用方法。csvデータ取得に向いています

'csvやWrite#の文字列を取得
Input #1, 文字列変数

開いたテキストデータから文字列を取得するには「Input#」ステートメントを使用します
主にcsvデータやWrite#ステートメントで追記した文字列を取得する際に使用します

このステートメントでは「,」や改行でデータが区切られます
データは、引数に指定した変数に代入されます

Input #1, 文字列変数

引数1つ目の「#1」はOpenステートメントで開く際に指定したファイル番号になります
ファイル番号に関する解説は以下の記事を確認してください

2つ目の引数「文字列変数」は、取得したデータを代入する変数名を指定します
この引数はリストデータになるので、追加で指定することが可能です

取得時の動き

Sub 処理テスト()
    Open ThisWorkbook.Path & "\新しいテキスト ドキュメント.txt" For Input As #1
    Do Until EOF(1) = True
        Input #1, 文字列変数
        Debug.Print 文字列変数
    Loop
    Close #1
End Sub

1要素ずつ取得して、それをイミディエイトに出力していく処理です
このコードを以下の文字列で実行して、取得の動きを確認します

取得を行うテキストデータ
取得を行うテキストデータ

この画像のテキストデータをコード実行し、取得を行います
その取得後の出力結果が以下の画像になります

取得を実行したイミディエイトの出力結果
イミディエイトの出力結果

上下の画像を見比べて取得のされ方を確認してください

「新しいテキスト1行目,青」
これは「,」でデータが区切られるので「新しいテキスト1行目」と「青」に分割して取得されます

「”新しいテキスト2行目”,黄」
これは1つ目に取得される文字列には、「”」で囲まれた文字列表現になっています
このステートメントでは「”」は除外して中の文字列を取得します
2つ目は「,」で区切られるので上記と同じです

「”新しいテキスト3行目,赤”」
これは1行まるごと「”」で囲まれています
この状態では、「,」は文字列と認識されるのでデータが分割されることはなく1行まるごとが取得されます

Input #1, 文字列変数(0), 文字列変数(1)

このコードのように、取得させる変数を「,」で区切って追加することでまとめて取得する事が可能です
上記の「新しいテキスト1行目,青」を取得させた場合は、1回の取得で「青」までの2つを取得できます
とはいえ、分割して一括取得はSplit関数を応用するほうが簡単です

このステートメントで、取得を行う際に文字列がcsvのようにある程度きちんとしたデータではなかった場合不安定かもしれません
そういったテキストデータを扱う際には以下のLineInput#ステートメントの方がいいかもしれません

テキストデータを閉じる

Openステートメントで開かれたテキストデータはCloseステートメントで終了させます

'番号1のファイルを閉じる
Close #1
'全てのファイルを閉じる
Close

Openステートメントで開かれたテキストデータを閉じるには「Close」ステートメントを使用します

このコードを実行する事で、開いているデータが閉じられるので新規作成されていた場合はファイルが実際に作成されます

また、指定番号の使用も終了します
なのでここで終了したファイル番号は以降で同じ番号が使用できるようになります

基本的には、番号を指定して終了します
使用していない番号を指定してもエラーは発生せず、開いている分は当然終了することはありません
番号は省略が可能で、省略した場合は開かれているデータが全て終了されます

Openステートメントで開いたデータはこのコードで必ず終了するようにしてください