Visual Basic 初級講座 [改訂版]
VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

第13回 繰り返し処理 For Next

2020/5/31

この記事が対象とする製品・バージョン

VB2019 Visual Basic 2019 対象です。
VB2017 Visual Basic 2017 対象です。
VB2015 Visual Basic 2015 対象です。
VB2013 Visual Basic 2013 対象です。
VB2012 Visual Basic 2012 対象です。
VB2010 Visual Basic 2010 対象です。
VB2008 Visual Basic 2008 対象です。
VB2005 Visual Basic 2005 対象です。
VB.NET 2003 Visual Basic.NET 2003 対象外ですがほとんどの説明があてはまるので参考になります。
VB.NET 2002 Visual Basic.NET (2002) 対象外ですがほとんどの説明があてはまるので参考になります。
VB6対応 Visual Basic 6.0 × 対象外です。

 

目次

 

1.For ~ Nextの概要

For ~ Next (読み方:For=フォー、Next=ネクスト)を使うと指定した回数処理を繰り返して実行することができます。

たとえば、次の例ではMsgBoxを5回実行します。

VB2015 VB2017 VB2019

For i As Integer = 0 To 4
    MsgBox($"{i}回目です。")
Next

この例は $ から始まる補間文字列を使用しているためVB2015以上でないと実行できませんが、For ~ Next自体はすべてのバージョンのVBで使用できます。

この例を実行すると「0回目です。」、「1回目です。」、「2回目です。」、「3回目です。」、「4回目です。」とメッセージが順番に合計5回表示されます。

For ~ Nextの間には何行でもプログラムを記述することができ、その全部が繰り返して実行されます。

 

なお、For Each ~ Next のように「Each」(イーチ)が入っている構文がありますが、これは今回説明するFor ~ Nextとは別物です。

 

2.For ~ Nextによる繰り返しの実践

2-1.1個の円

For ~ Nextははじめての人には複雑に見えると思うので、構文を説明する前に実際にサンプルプログラムで少し肩慣らしして見ましょう。

実際に一緒に試してみることをお勧めします。

新しいWindowsフォームアプリケーションを.NET Frameworkで作成し、プロジェクト名は ForNextSample としてください。

簡単なコンピューターグラフィックス(CG)を書いてみます。

Windowsフォームアプリケーションでは簡単にグラフィックスを描画するにはフォームの Paintイベントを使用します。

たとえば、次のように記述すると赤い円が表示されます。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '赤い円を描画します。
    e.Graphics.DrawEllipse(Pens.Red, 50, 60, 200, 160)
End Sub

少しつぶれた円にしてみました。

円を描画するメソッドは DrawEllipseメソッド(読み方:DrawEllipse=ドロゥエリプス)です。このメソッドは第1引数で使用するペンを指定します。このペンによって色や線の種類が変わります。

他の4つの引数は円の座標を外接四角形を使って表しています。コンピューターの世界ではフォームの左上が原点(0, 0)でY座標は下側はプラスです。

最初の2つの数字は外接四角形の左上の点の座標です。残りの2つの数字はこの四角形の幅と高さです。

2-2.5個の円

For ~ Next を使わないで描く

これだけの知識があれば、詳しい意味はわからなくても好きな座標に円を描画する方法はわかると思います。

それでは、座標をちょっとずつ変えて、5個の円を書いて見ましょう。今度はまん丸にしてみます。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '5つの赤い円を描画します。
    e.Graphics.DrawEllipse(Pens.Red, 10, 10, 200, 200)
    e.Graphics.DrawEllipse(Pens.Red, 20, 20, 200, 200)
    e.Graphics.DrawEllipse(Pens.Red, 30, 30, 200, 200)
    e.Graphics.DrawEllipse(Pens.Red, 40, 40, 200, 200)
    e.Graphics.DrawEllipse(Pens.Red, 50, 50, 200, 200)

End Sub

次のようになります。

For ~ Next で書き換える

DrawEllipseを5回実行しているので、For ~ Next を使った繰り返しにしたいところです。

次のように書くと DrawEllipseを5回実行してくれるのですが、全部座標が同じなので結果は1つの円に見えます。

 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '5回DrawEllipseを実行しますが、同じ座標なので結果は1つの円に見えます。
    For i As Integer = 0 To 4
        e.Graphics.DrawEllipse(Pens.Red, 10, 10, 200, 200)
    Next

End Sub

For ~ Next の構文ではカウンター変数と呼ばれる変数を必ず使用することになっており、このカウンター変数を使うと今何回目の実行なのかわかります。

この例ではカウント変数は i です。カウンター変数の初期値は 0 です。カウンター変数は繰り返すごとに自動的に +1 されていき、4 になるまで繰り返しを実行します。

つまり、i  の 値が最初は0、次は1、その次は2, その次は3, その次は4 、ここでFor ~ Next 終了。全部で5回実行されるというわけです。For ~ Nextではこのカウンター変数をうまく活用するのがコツです。

今回はカウンター変数に1をプラスして10倍した値を座標にすれば毎回違う座標で実行されるようになります。

繰り返し回数 カウンター変数 i の値 (i + 1) * 10 の値
1回目 0 10
2回目 1 20
3回目 2 30
4回目 3 40
5回目 4 50

これをプログラムで書くと次のようになります。

 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '5つの赤い円を描画します。
    For i As Integer = 0 To 4
        e.Graphics.DrawEllipse(Pens.Red, (i + 1) * 10, (i +1)  * 10, 200, 200)
    Next

End Sub

 

カウンター変数を工夫する

カウンター変数 i の初期値を 1 にして To で指定している終了値を 5 にすれば、プログラム内で + 1 する必要がなくなり、少しプログラム量が減ります。やってみましょう。

※ To で指定している値の呼び方は特に決まっておらず「終了値」という表現は一般的なものではないかもしれません。それでも呼び方がないと不便なのでこの記事では終了値と呼びます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '5つの赤い円を描画します。
    For i As Integer = 1 To 5
        e.Graphics.DrawEllipse(Pens.Red, i * 10, i * 10, 200, 200)
    Next

End Sub

これで同じ結果になりますが、世の中のプログラマーはカウンター変数が 0 から始まるのに慣れている人が多くいるようで、多少プログラム量が増えても、カウンター変数の初期値は 0 にするプログラマーが多いようです。

 

Stepを使う

キーワード Step(読み方:Step = ステップ)を使うと、カウンター変数の増量も指定できます。これを使って 10 ずつカウンター変数が増加していくようにすると、 * 10 する必要もなくなりさらにプログラム量が減ります。この場合、初期値を 10 、終了値 を 50 にすることも必要です。

 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '5つの赤い円を描画します。
    For i As Integer = 10 To 50 Step 10
        e.Graphics.DrawEllipse(Pens.Red, i, i, 200, 200)
    Next

End Sub

最初のプログラムよりだいぶ見やすくなりました。

 

2-3.5個の同心円

For ~ Next を使わないで描く

せっかくなのもう少し遊んでみましょう。

この例のように5回くらいの繰り返しならコピー&貼り付けで書くことも大して苦ではないのですが、For ~ Nextは100回でも1000回でも簡単に繰り返せます。その威力がわかるプログラムを作ってみます。

なお、5回くらいの繰り返しでも、後で機能追加や修正が必要になったときのことを考えると、For ~ Nextなどの繰り返し処理にしておいたほうが断然楽です。

 

今度は中心が同じで大きさが違う円を書いてみます。

一番外側の円はフォームの描画領域にぴったりのサイズにしてみます。

フォームの描画領域のサイズはClientSizeプロパティ(読み方:ClientSize=クライアントサイズ)で取得できます。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '描画領域の幅を取得
    Dim Yoko As Integer = Me.ClientSize.Width - 1

    '描画領域の高さを取得
    Dim Tate As Integer = Me.ClientSize.Height - 1

    '5つの緑の同心円を描画します。
    e.Graphics.DrawEllipse(Pens.Green, 0, 0, Yoko, Tate)
    e.Graphics.DrawEllipse(Pens.Green, 10, 10, Yoko - 20, Tate - 20)
    e.Graphics.DrawEllipse(Pens.Green, 20, 20, Yoko - 40, Tate - 40)
    e.Graphics.DrawEllipse(Pens.Green, 30, 30, Yoko - 60, Tate - 60)
    e.Graphics.DrawEllipse(Pens.Green, 40, 40, Yoko - 80, Tate - 80)

End Sub

フォームのサイズを変えると、円のサイズも変わるようにフォームのサイズ変更の時に発生するResizeイベント(読み方:Resize=リサイズ)には次のように記述してください。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
    Me.Invalidate()
End Sub

Invalidateメソッド(読み方:Invalidate=インバリデイト)は、フォームの再描画を発生させるメソッドです。これによりフォームのサイズを変更すると、フォーム全体に対してPaintイベントが再度発生します。このあたりのグラフィックスの理屈はわかりにくいのですが、グラフィックスはWindowsと連携して行う必要があり、Windowsに再描画が必要と認識してもらう必要があります。Invalidateメソッドはそれを行います。今回のテーマはFor ~ Nextなのでこのあたりの理屈の詳細は理解する必要はありません。

それから、プロパティウィンドウを使ってFormのDoubleBufferedプロパティ(読み方:DoubleBuffered=ダブルバッファード)を True にしておいてください。このプロパティがTrueだと、内部処理でダブルバッファリングという手法を使って再描画が実行されます。この方式はグラフィックスのちらつきを抑えてきれいに再描画を実行してくれるので、変化するグラフィックスを扱うプログラムでは必須です。

前置きが長くなりましが、実行して、マウスでフォームのサイズを変更すると円のサイズも変わることが確認できると思います。

 

For ~ Next で書き換える

今回のテーマであるFor ~ Nextを使ってプログラムを書き換えてみましょう。次のようになります。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '描画領域の幅を取得
    Dim Yoko As Integer = Me.ClientSize.Width - 1

    '描画領域の高さを取得
    Dim Tate As Integer = Me.ClientSize.Height - 1

    '5つの緑の同心円を描画します。
    For i As Integer = 0 To 4
        Dim deltaLocation As Integer = i * 10
        Dim deltaSize As Integer = i * 20
        e.Graphics.DrawEllipse(Pens.Green, deltaLocation, deltaLocation, Yoko - deltaSize, Tate - deltaSize)
    Next

End Sub

 

2-4.50個の同心円

ここまでできれば、いくつでも同心円を描けます。

たとえば、To で指定している終了値を4から50に変更すると次のような模様が現れます。

このプログラムでは外接四角形の大きさをひき算で計算しているところがあり、カウンター変数の値が大きくなると値がマイナスになってしまいます。その場合エラーになるのではなく、通常と反対の方向に大きさをもった外接四角形という意味になるのでこのような模様になります。

 

2-5.200個の色彩豊かな同心円

最後にカウンター変数を使って色も変化させて見ます。フォームを大きなサイズにしてもきれいに見えるようにTo で指定している終了値は 999 にするなど、少し数値を微調整します。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '描画領域の幅を取得
    Dim Yoko As Integer = Me.ClientSize.Width - 1

    '描画領域の高さを取得
    Dim Tate As Integer = Me.ClientSize.Height - 1

    '200個の同心円を描画します。
    For i As Integer = 0 To 999 Step 5
        Dim deltaLocation As Integer = i * 2
        Dim deltaSize As Integer = i * 4
        Dim color As Color = Color.FromArgb(i Mod 200 + 55, i Mod 160 + 95, i Mod 123 + 122)
        Dim pen As New Pen(color)

        e.Graphics.DrawEllipse(pen, deltaLocation, deltaLocation, Yoko - deltaSize, Tate - deltaSize)
    Next

End Sub

大分カラフルになりました。マウスでフォームの大きさを単純な円が複雑に絡み合って意外な模様を見せてくれます。綺麗なのでぜひ実際にやってみてもらいたいです。

フォームの大きさを変えたときに円がちらつくようであればフォームのDoubleBufferedプロパティをTrueにするのを忘れているかもしれません。確認してみてください。

最後の例では初期値 0 、終了値 999、Step 5なので全部で200個の円が描画されることになります。For ~ Nextを使うことで簡単に処理を繰り返せること、それに、まったく同じ処理を繰り返すわけではなく、カウンター変数を使って処理に変化がつけられることを理解していただけたと思います。

 

発展 発展学習  -  アニメーション

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

アニメーションにすると面白かもと思ったのでちょっと作ってみました。次のプログラムを実行すると円が少しずつ移動します。パソコンのスペックが低い場合、ウィンドウサイズを大きくするとぎこちなくなるかもしれません。

この例ではタイマーを生成し、50ミリ秒(=0.05秒)ごとにtimer_Tickを呼び出します。timer_Tickの中ではdeltaを1ずつプラスしていきます。そして、Me.Invalidateを呼び出します。Invalidateは表示領域を無効化する命令で、これが実行されるとWindowsが再描画の実行を指示します。結果としてPaintイベントが呼び出されます。つまり、0.05秒ごとにdeltaを1プラスしてPaintイベントを呼び出すプログラムというわけです。

Paintイベントの中では delta を使って位置をサイズを計算しているため、0.05秒ごとに違った位置とサイズに円が描画されます。これを連続して実行することで人間の目には円が動いて見えるという仕掛けです。

 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    Me.DoubleBuffered = True
    Dim timer As New Timer
    timer.Interval = 50
    AddHandler timer.Tick, AddressOf timer_Tick
    timer.Enabled = True
End Sub

Private delta As Integer
Private Sub timer_Tick(sender As Object, e As EventArgs)
    delta += 1
    If delta = 800 Then
        delta = 0
    End If
    Me.Invalidate()
End Sub

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint

    '全体を黒で塗りつぶします。
    e.Graphics.Clear(Color.Black)

    '描画領域の幅を取得
    Dim Yoko As Integer = Me.ClientSize.Width - 1

    '描画領域の高さを取得
    Dim Tate As Integer = Me.ClientSize.Height - 1

    '200個の同心円を描画します。
    For i As Integer = 0 To 999 Step 5
        Dim deltaLocation As Integer = i * 2 + delta
        Dim deltaSize As Integer = i * 4 + (delta * 2)
        Dim color As Color = Color.FromArgb(i Mod 200 + 55, i Mod 160 + 95, i Mod 123 + 122)
        Dim pen As New Pen(color)

        e.Graphics.DrawEllipse(pen, deltaLocation, deltaLocation, Yoko - deltaSize, Tate - deltaSize)
    Next

End Sub

 

 

3.For ~ Next の書き方

3-1.構文

For Next構文をまとめると次の通りです。

赤い部分は必ず記述する必要があります。その他の部分、たとえば、Step は必要があるときだけ記述します。

VB6 VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For カウンター変数 = 初期値 To 終了値 Step カウンター変数の増加量

     繰り返して実行する処理

Next カウンター変数

Visual Studioでは For に続けて TAB を2回押すと、For ~ Nextの構文を自動的に挿入することができます。

 

3-2.カウンター変数

カウンター変数の書き方は3パターンあります。

パターン1:外部の変数の利用

別途どこかで宣言した変数(この場合はすぐ上で宣言している変数 i )を利用する場合の書き方は次の通りです。この書き方は古い書き方でありVB2003以降はお勧めできません。

VB6 VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim i As Integer
For i = 0 To 9
    '繰り返して実行する処理
Next

パターン2:For とともに明示的に宣言

As 型名をつけるとFor文の中でカウンター変数を明示的に宣言できます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 0 To 9
    '繰り返して実行する処理
Next

パターン3:カウンター変数の型推論

カウンター変数の型を推論させることができます。この場合、初期値、終了値、Stepで指定した増加量の値からカウンター変数の値が推論されます。下記の例ではカウンター変数 i は Integer になります。

 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i = 0 To 9
    '繰り返して実行する処理
Next

 

For ~ Nextだけ見ると、パターン1とパターン3は同じです。Forより前の部分で変数が宣言されているかされていないかの違いです。

 

カウンター変数には数値以外の型を使用できる場合もありますが、混乱を招くので数値型を使用することを強く推奨します。

 

3-3.Stepによるカウントダウン

Stepでの増加量にはマイナスを指定することでカウンター変数をカウントダウンさせることもできます。この場合、初期値が1周ごとにマイナスされていき終了値以下になった時点で終了します。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 9 To 0 Step -1
    '繰り返して実行する処理
Next

この手法はFor ~ Nextを使って何かの集合を1つずつ削除していく場合に使われることがあります。

この使い方は役に立つ例を見てみないとピンと来ないと思うので、とりあえず今は必要であればカウントダウンもできるということだけ覚えておいてください。

 

3-4.Exit For

Exit Forを使うと、ループをすぐに終了できます。カウンター変数や終了値とは関係なくFor ~ Nextにより繰り返しが終了します。

※「ループ」という言葉は「繰り返し」と同じ意味です。

たとえば、次の例は、i の値が2になったときにForでのループを終了します。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 0 To 4
    If i = 2 Then
        Exit For
    End If
    MsgBox(i.ToString)
Next

この例はExit Forの説明用であり、意味はありません。2のときに終了させることが決まっているのならば To 2 と書いたほうがシンプルです。

この例では「0」、「1」と2回メッセージが表示されます。

 

3-5.Continue For

Continue For(読み方:Continue=コンティニュー)を使うと、その周の実行を飛ばして次の周を実行します。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 0 To 4
    If i = 2 Then
        Continue For
    End If
    MsgBox(i.ToString)
Next

この例では「0」、「1」、「3」、「4」と4回メッセージが表示されます。i が 2 のときは Continue Forが実行されるので、その下は飛ばされて i = 3の周が開始されます。

メモ メモ  -  わかりにくければステップ実行で動きを確かめて見ましょう。

Forの行にブレークポイントを設定して、ステップ実行するとどのような動きになるのか簡単に理解できます。百聞は一見にしかずというやつで、文章の説明よりもわかりやすいです。

ステップ実行の方法はこちら をご覧ください。

 

3-6.カウンター変数の直接操作

カウンター変数に直接値を設定することでExit ForやContinue Forでは実現できないフローを実現することができます。ただ、プログラムがわかりにくくなるので多用することは禁物です。

 

たとえば、次の例では最初の周でカウンター変数に 10 を設定しているため、カウンター変数の値は1周目で終了値以上になります。そのため2周目は実行されません。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 0 To 9
    i = 10
    Debug.Print(i.ToString)
Next

ただループを終了させることだけが目的であれば、後で説明するExit Forを使えばよく、カウンター変数を直接操作するのは悪手です。

カウンター変数を直接操作する場合に多いのは +1、+2 して何周か飛ばしたり、-1, -2 して何周か戻したりする使い方です。

次の例を実行すると出力ウィンドウのデバッグに 1,3,5,7,9が表示されます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 0 To 9
    i = i + 1
    Debug.Print(i.ToString)
Next

動きがわかりにくければステップ実行しながら、自動変数ウィンドウまたはローカルウィンドウで i の値を確認してみてください。

ローカルウィンドウはデバッグ中にローカル変数の値を表示するウィンドウです。

 

 

4.二重ループ

4-1.基本的な二重ループ

For ~ NextのなかにFor ~ Nextを記述してネスト(入れ子)すると二重ループになります。

その中にさらに For ~ Next を書くと3重、4重のループを記述できますが、わかりにくくなるのであまりネストすることはお勧めできません。

ネストする場合は外側のFor文と内側のFor文でカウンター変数を変える必要があります。よく使われるのは、i, j, k ... というアルファベットです。

 

次の例はかけ算の式と答えをデバッグに出力します。

VB2015 VB2017 VB2019

For i As Integer = 1 To 2
    For j As Integer = 1 To 3
        Debug.Print($"{i} × {j} = {i + j}")
    Next
Next

デバッグはVisual Studioの[デバッグ]メニューの[ウィンドウ]- [出力] で出力ウィンドウを表示して、出力元欄を「デバッグ」にすると表示できます。

この例は $ を使った補間文字列を使っているのでVisual Basic 2015以上でないと実行できませんが、For ~ Nextのネスト自体はどのバージョンのVBでも実行できます。

メモ メモ  -  Next の省略

上記の例でもそうなのですが、多重ループの場合、Nextとだけ書いてある行が2行、3行と続く場合があります。この場合、ちょっとプログラムが見にくいのでカウンター変数をそえて、どのForに対するNextなのかの目印を記述することもできます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 1 To 2
    For j As Integer = 1 To 3
        '繰り返して実行する処理
    Next j
Next i

さらに、カウンター変数をカンマで区切ることで Next を1つにまとめることもできます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For i As Integer = 1 To 2
    For j As Integer = 1 To 3
        '繰り返して実行する処理
Next j, i

でも、これをやると、なんだか Next を1個書き忘れているみたいにも見えるので逆にわかりにくいです。この書き方はしないようにしましょう。

 

4-2.実例

pingを探す

複雑になりますが、もう少し実践的な例を使って説明しましょう。

初級講座ではまだ説明していない要素も登場しますが、参考になると思いますので実際に手を動かしてプログラムを入力・実行しながら読んでみてください。

 

まず、新しいWindows フォームアプリケーションのプロジェクトを作成して、名前はForNextPingとしましょう。

このプログラムでは ping の場所を探します。

 

pingはご存知でしょうか?

PowerShellかコマンドプロンプトを開いて、ping www.microsoft.com などと実行すると指定した場所にアクセスできるか試してくれるツールです。ネットワークのトラブルシューティングでよく使用します。

このようにコマンド名でいきなりコマンドを使用できるのは、フォルダーがWindowsの環境変数pathに事前に登録されているからです。もし、フォルダーが環境変数Pathに登録されていない場合、C:\xxxx\xxxx\ping.exe などのようにコマンド名はファイル名としてフルパスで指定する必要があります。(またはその場所をカレントフォルダーにするかなど)

 

環境変数の取得

環境変数は Environment.GetEnvironmentVariableメソッド (読み方:Environment=エンバイロメント、GetEnvironmentVariable=ゲットエンバイロメントバリエイブル)で取得できます。

環境変数 path の内容を取得して表示するプログラムは次のように簡単に書けます。

フォームにButtonを1つ貼り付けて試してみてください。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Debug.Print(path)

これを実行すると出力ウィンドウには次のような長い文字列が表示されます。この内容は環境によって異なります。

C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;C:\windows\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Users\rucio\AppData\Local\Microsoft\WindowsApps;

メモ メモ  -  環境変数

環境変数とは空気のようにそこに存在してる名前と値の組み合わせのことを言います。代表的なものはWindowsで定義されているシステム環境変数です。この環境変数はマイコンピューターのプロパティからシステムの詳細設定画面で「環境変数」ボタンをクリックすると確認・編集できます。

下記のプログラムを実行するとその状況(コンテキスト)で使用できる全環境変数の一覧を確認できます。初級講座でまだ説明していないFor Eachを使用しています。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For Each env As DictionaryEntry In Environment.GetEnvironmentVariables
    Debug.Print(env.Key.ToString & " = " & env.Value.ToString)
Next

 

pathの分解

環境変数 path には ; で区切って複数のパスが登録されています。 Splitメソッド(読み方:Split=スプリット)を使用すると ; で分解して配列にすることができます。

次のようにします。

配列は型の後ろに () を付けて表現します。この例では変数 paths が Stringの配列です。

VB.NET 2002 VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

Debug.Print(paths(0))
Debug.Print(paths(1))
Debug.Print(paths(2))
'・・・

この例では、最初の3つのパスを表示するようにしています。私の環境では次のように出力されます。

C:\windows\system32
C:\windows
C:\windows\System32\Wbem

このように配列の後ろに (0)、(1)、などと指定すると「1つ目の」、「2つ目の」という意味なります。このカッコの中の数字を添え字(そえじ)またはインデックスと呼びますが、これがFor ~ Nextのカウンター変数ととても相性が良いわけです。

次のように書くと、すべてのパスを出力できます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

For i As Integer = 0 To paths.Count - 1
    Debug.Print(paths(i))
Next

 

ファイルの数

フォルダーに含まれているファイルの一覧はDirectoryクラスのGetFilesメソッドで取得できます。このメソッドの戻り値はファイルのフルパスを表す配列です。

上の例を改造してそれぞれのフォルダーのファイル数を表示するようにしてみます。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

For i As Integer = 0 To paths.Count - 1
    Dim envPath As String = paths(i)

    Dim files As String() = IO.Directory.GetFiles(envPath)

    Debug.Print(envPath & " 内のファイル数:" & files.Count.ToString)
Next

これを実行すると、エラーになる人もいると思います。

環境変数 path には実在しないパスが登録されている場合があるからです。実在しないパスのファイルをGetFilesメソッドで取得しようとするとエラーになるのです。

そこで、GetFilesメソッドを使用する前にフォルダーが実在するか確認することにしましょう。DirectoryクラスのExistsメソッドで確認できます。

存在しないフォルダーは対象にしなくて良いので、Continue Forで次のフォルダーの処理にジャンプすることにしましょう。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

For i As Integer = 0 To paths.Count - 1
    Dim envPath As String = paths(i)

    'フォルダーが存在しない場合、次の周を実行する。
    If IO.Directory.Exists(envPath) = False Then
        Continue For
    End If

    Dim files As String() = IO.Directory.GetFiles(envPath)

    Debug.Print(envPath & " 内のファイル数:" & files.Count.ToString)
Next

実行すると出力ウィンドウのデバッグに次のように表示されます。

C:\windows\system32 内のファイル数:2748
C:\windows 内のファイル数:21
C:\windows\System32\Wbem 内のファイル数:161
C:\windows\System32\WindowsPowerShell\v1.0\ 内のファイル数:22
C:\Program Files\dotnet\ 内のファイル数:3
C:\Program Files\Microsoft SQL Server\130\Tools\Binn\ 内のファイル数:1
C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\ 内のファイル数:4
C:\Users\rucio\AppData\Local\Microsoft\WindowsApps 内のファイル数:3

 

ファイル名の判断

今度はファイル数を表示するのではなく、ループをまわしてファイル名を1個ずつ確認してきましょう。

ファイル名は Pathクラスの GetFileNameメソッドで取得できます。このメソッドはフルパスからフォルダー名を取り除いてファイル名だけにします。

そのファイルがpingであるかどうかのIf文はどう書けばよいでしょうか?

以上を考えると、ファイル名を大文字に変換して "PING.EXE" または "PING.COM" であるかという条件が良さそうです。

pingを発見したら後続の処理を続ける必要はないのでExit Forでループを脱出するものとします。

 

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

For i As Integer = 0 To paths.Count - 1
    Dim envPath As String = paths(i)

    'フォルダーが存在しない場合、次の周を実行する。
    If IO.Directory.Exists(envPath) = False Then
        Continue For
    End If

    Dim files As String() = IO.Directory.GetFiles(envPath)

    Debug.Print(envPath & " 内のファイル数:" & files.Count.ToString)

    For j As Integer = 0 To files.Count - 1
        Dim fileName As String = IO.Path.GetFileName(files(j))

        If fileName.ToUpper = "PING.EXE" OrElse fileName.ToUpper = "PING.COM" Then
            Debug.Print("★" & files(j) & " 発見!")
            Exit For
        End If
    Next

Next

これを実行すると次のように表示されます。

C:\windows\system32 内のファイル数:2748
★C:\windows\system32\PING.EXE 発見!
C:\windows 内のファイル数:21
C:\windows\System32\Wbem 内のファイル数:161
C:\windows\System32\WindowsPowerShell\v1.0\ 内のファイル数:22
C:\Program Files\dotnet\ 内のファイル数:3
C:\Program Files\Microsoft SQL Server\130\Tools\Binn\ 内のファイル数:1
C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\ 内のファイル数:4
C:\Users\rucio\AppData\Local\Microsoft\WindowsApps 内のファイル数:3

無事、「C:\windows\system32」にPING.EXE があることを発見できました。大文字のファイル名だったんですね。

 

ネストされたFor ~ Next からの脱出

でも、ちょっとおかしいですね。pingを発見したらExit Forをしているのに、その後もメッセージが出力されているのはどういうことでしょうか?

 

この例は、For ~ Next のなかに、For ~ Next がネストされている構造になっています。Exit Forは内側のFor ~ Nextで使用されているため、内側からは脱出できるのですが、外側のFor ~ Nextは引き続き実行されるというわけです。

今回の例は、後続の処理がないため、Exit Forの代わりの Return (読み方:Return=リターン)を使用すれば解決します。Returnはそのプロシージャの処理を終了する命令です。ボタンのクリックイベントハンドラー(Button1_Click)自体から脱出するというわけです。

今回はそれでよいですが、外側のFor ~ Nextの後続に処理があってそれを実行したい場合はどうしたらよいでしょうか。

その場合はGoTo(読み方:GoTo=ゴートゥー)を使用します。

Gotoは指定した場所に実行をジャンプする機能です。場所を指定するために、場所にはラベルという目印をつけておく必要があります。ラベルは xxxxx: のような形で末尾にコロンをつけて書きます。そして、Goto xxxxx と、ラベル名を指定するとそのラベルの位置に実行がジャンプするというわけです。

これを使うと次のようになります。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim path As String = Environment.GetEnvironmentVariable("path")

Dim paths As String() = Split(path, ";")

For i As Integer = 0 To paths.Count - 1
    Dim envPath As String = paths(i)

    'フォルダーが存在しない場合、次の周を実行する。
    If IO.Directory.Exists(envPath) = False Then
        Continue For
    End If

    Dim files As String() = IO.Directory.GetFiles(envPath)

    Debug.Print(envPath & " 内のファイル数:" & files.Count.ToString)

    For j As Integer = 0 To files.Count - 1
        Dim fileName As String = IO.Path.GetFileName(files(j))

        If fileName.ToUpper = "PING.EXE" OrElse fileName.ToUpper = "PING.COM" Then
            Debug.Print("★" & files(j) & " 発見!")
            GoTo EndOfFor
        End If
    Next

Next

EndOfFor:

この例ではラベルは EndOfFor: という名前にし、一番外側のNextのすぐ下に設置しました。Exit Forの代わりにGoto EndOfFor とすることで見事一番外側のForから脱出できます。

これを実行すると次の2行だけが表示され、これ以上処理されていないことがわかります。

C:\windows\system32 内のファイル数:2748
★C:\windows\system32\PING.EXE 発見!

Gotoは機能上はどこでも使用できるのですが、今回の例のように多重にネストされた構造の内側から脱出するとき以外は使わないことが共通認識となっています。それ以外の場所でGoToを使うと、それがうまく機能していたとしても先輩に怒られます。

GoToはプログラムの歴史の中ではかなり古い存在で、1970年頃には、GoToを使ってあっちにジャンプ、こっちにジャンプするプログラムがたくさんあったそうです。GoToであっちこっちにジャンプするため、少しみただけだとどういう順番に何が実行されるのかとてもわかりにくいプログラムが増えて問題になったらしいのです。どういう順番で実行されるかわかりにくいプログラムだと修正するときにバグを埋め込んでしまうリスクが高くなります。せっかく、For ~ Nextなどのフローを制御する構造があるのだから、それを使うべきだという意見が大勢を占め、この反省を活かして、それ以降、プログラムで他の手段があるのにGoToを使うことはNGと認識されるようになりました。

そしてついに1995年に登場したプログラミング言語JavaとPHPではGoToは実装されませんでした。(ただし、PHPに関しては2009年のバージョン 5.3で実装されました。)

VBやC#ではGoToは使用できるもののこのような事情で他に手段がある場合は使うべきはないものと認識されています。

発展 発展学習  -  where.exe ping

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

PowerShellまたはコマンドプロンプトで where.exe ping と入力すると、PING.EXE の場所を確認できます。

 

5.列挙型のループ

For ~ Next で、カウンター変数の型に列挙型を使用すると、その列挙型で定義されている値でループすることができます。

わかりやすい例では曜日を表す列挙型 DayOfWeek (読み方:DayOfWeek=デイオブウィーク)があります。

これを使って月曜日から金曜日までループするには次のようにします。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For day As DayOfWeek = DayOfWeek.Monday To DayOfWeek.Friday
    Debug.Print(day.ToString)
Next

これを実行すると、出力ウィンドウのデバッグには次の通り表示されます。

Monday
Tuesday
Wednesday
Thursday
Friday

列挙体の各値には数値が割り当てられており、たとえばMondayは1、Tuesdayは2です。MondayからFridayまでループするということは1から5までループするという意味です。

面白い使い方だと思うのですが、列挙体にどういう値がどういう順で割り当てられているかはわかりにくいことが多く、この機能を実際に有効に使っているプログラムは私はみたことがありません。

For Eachを使っても列挙体の値を列挙することができます。For Eachについては別の回で扱いますが、簡単に説明すると含まれているすべての値をつかって1回ずつ実行するループです。

VB.NET 2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

For Each day In [Enum].GetValues(GetType(DayOfWeek))
    Debug.Print(day.ToString)
Next

この例を実行すると出力ウィンドウのデバッグには次の通り表示されます。

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

For Eachを使うと、初期値と終了値を指定することはできないので、全部が列挙されますが、列挙体に割り当てられている値を知らなくてもとりあえず全部の値を処理することができるという点ではこちらの方が使いやすいです。こちらを使っているプログラムはたまに見かけます。