Wrox Home  
Search
by Adrian Kingsley-Hughes, Kathie Kingsley-Hughes, Daniel Read
July 2004, Paperback


Excerpt from VBScript Programmer's Reference, 2nd Edition

Loop Constructs: Do Loop

Do loop is the most versatile of all of the loop constructs. This is because you can easily make it loop as many times as you like based on any criteria you like. (However, you'd have to go through some trouble to use it to traverse a collection — For Each…Next is much better for that.) The power of the Do loop is in the use of the While and Until keywords. You can use While or Until at either the beginning of the loop or the end of the loop to control whether the loop will go around again. Let's look at a simple script that uses a Do loop (DO_WHILE.VBS).

Option Explicit

Dim boolLoopAgain
Dim lngLoopCount
Dim strResponse

boolLoopAgain = False
lngLoopCount = 0
Do
    boolLoopAgain = False
    lngLoopCount = lngLoopCount + 1

    strResponse = InputBox("What is the magic word?")
    If UCase(Trim(strResponse)) = "PLEASE" Then
        MsgBox "Correct! Congratulations!"
    Else
        If lngLoopCount < 5 Then
            MsgBox "Sorry, try again."
            boolLoopAgain = True
        Else
            MsgBox "Okay, the word we wanted was 'Please'."
        End If
    End If
Loop While boolLoopAgain

Using a Do loop in this way to process user input is a common technique. You need to ask the user something, but he or she might enter illegal data. You need a way to check the input and, if necessary, loop back and ask the user to enter it again.

Notice how the Do statement marks the beginning of the loop block, and how the Loop statement defines the end of the block. The While statement, however, places a condition on the Loop statement. The loop will only go around again if the expression following the While statement is True. In this case, our expression is a variable called boolLoopAgain, which has the Boolean subtype, but it could be any expression that evaluates to or returns a True or False response.

Notice also how we initialize the boolLoopAgain variable to False before the loop starts. This accomplishes two things: it establishes the subtype of the variable as Boolean, and it guarantees that the loop will only go around again if some piece of code inside the loop explicitly sets the variable to True. If the user guesses wrong, then we set  boolLoopAgain to True, guaranteeing that the loop will go around at least one more time and so we can ask the user to guess again.

Finally, notice how we use a loop counter variable, lngLoopCount, to make sure that the loop does not go around forever and drive the user crazy if he or she can't guess the magic word. Using a loop counter variable is optional, and not part of the Do…Loop syntax, but it's a good idea if there's a chance that the loop might go around indefinitely.

Using this particular loop structure-with the Do statement by itself at the beginning, and the While condition attached to the Loop statement at the end-has an important implication: because we did not place a condition on the Do statement, the code inside the loop is guaranteed to execute at least once. This is what we want in this case, because if we did not execute the code at least one time, the user would never get asked the question "What is the magic word?"

Sometimes, though, you only want the code inside the loop to execute if some precondition is True; if that precondition is False, then you don't want the loop to execute at all. In that case, we can place the While statement at the beginning of the loop. If the Do While condition is False, then the loop will not go around even once.

In the following example, we are going to use the FileSystemObject to open a text file. We will access the text file using a TextStream object. When you open a file in the form of a TextStream object, the TextStream object uses a "pointer" to keep track of it's place in the file as you move through it. When you first open the file, the pointer is at the beginning of the file. (The pointer is not physically placed in the file-it exists only in memory in the TextStream object.) You can move through the file line by line using the TextStream.ReadLine method.

Each time you call ReadLine, the pointer moves one line down in the file. When the pointer moves past the last line in the file, the TextStream.AtEndOfStream property will have a value of True. That's when we know we are done reading the file. There is a possible problem, though: when we open a text file, we're not sure if it actually contains any data. It might be empty. If it is, then we don't want to call ReadLine, because this will cause an error. However, we'll know that the file is empty if the AtEndOfStream property is True right after opening the file. We can handle this nicely by placing the calls to ReadLine inside of a Do loop.

If you want to try out this code yourself, just create a text file and put the following lines in it (the downloadable code contains a file called TESTFILE.TXT).

Line 1
Line 2
Line 3
Line 4
Line 5

Save the file to your hard drive in the same location as the script below (DO_WHILE_READ_FILE.VBS). The script assumes that TESTFILE.TXT is in the same directory as the script file. While you're reading this code, don't worry if you're not familiar with the particulars of the FileSystemObject and TextStream objects, which are covered in detail in Chapter 7. Just pay attention to the way we use the While condition in conjunction with the Do statement.

Option Explicit

Dim objFSO
Dim objStream
Dim strText

Set objFSO = _
    WScript.CreateObject("Scripting.FileSystemObject")
Set objStream = objFSO.OpenTextFile("testfile.txt")
Set objFSO = Nothing

strText = ""
Do While Not objStream.AtEndOfStream
    strText = strText & objStream.ReadLine & vbNewLine
Loop
Set objStream = Nothing

If strText <> "" Then
    MsgBox strText
Else
    MsgBox "The file is empty."
End If

Running this code results in the dialog box shown in Figure 1.

VBScript, 2nd Edition, Programmer's Reference - Figure 1
Figure 1

You can see that by placing the While condition at the beginning of our loop, we can decide whether or not we want the loop to go around even once. If the file is empty, then we don't want to try reading any lines. Since there is no condition on the Loop statement, though, when the loop reaches the end, the code will jump back up to the Do line. However, if the Do While expression returns False, the loop will not execute again, and the code will jump back down to the line immediately following the Loop line.

The objStream.AtEndOfStream property will be True only when the end of the file is reached. As long as we have not reached the end of the file, we want to keep looping. If we start out at the end of the file because the file is empty, we don't want to loop at all.

Going back to our first Do loop example, for the record, note that we could have put the While statement with the Do in our first example and accomplished the same thing (DO_WHILE2.VBS).

Option Explicit

Dim boolLoopAgain
Dim lngLoopCount
Dim strResponse

boolLoopAgain = True
lngLoopCount = 0
Do While boolLoopAgain
    boolLoopAgain = False
    lngLoopCount = lngLoopCount + 1

    strResponse = InputBox("What is the magic word?")
    If UCase(Trim(strResponse)) = "PLEASE" Then
        MsgBox "Correct! Congratulations!"
    Else
        If lngLoopCount < 5 Then
            MsgBox "Sorry, try again."
            boolLoopAgain = True
        Else
            MsgBox "Okay, the word we wanted was 'Please'."
        End If
    End If
Loop

Compare our first Do loop example with this one. Both examples accomplish exactly the same thing: the loop executes at least once, and it will only loop again if the code inside the loop says that we should. The difference with this second technique is that we started off by initializing boolLoopAgain to True, which guarantees that the loop will execute at least once.

As you can see, the Do loop is quite versatile, and how you accomplish one thing or another is largely a matter of preference. That said, one could make a pretty good argument that the first version of this code is preferable because the Do statement all by itself makes it obvious that the loop is going to execute at least once, whereas this second example is a little bit tricky.

All else being equal, if there are two ways of coding something, the more explicit method is almost always preferable.

So the first question you need to answer when considering the use of the Do loop is, do I want the code to execute at least once, no matter what? If the answer to this question is yes, then it's best to place your condition at the end of the loop. Otherwise, put the condition at the beginning of the loop.

However, there is a second question: should you use the While statement for the condition, or its cousin, the Until statement? The answer to this second question is also largely a matter of preference. Although the While and Until statements are slightly different, they pretty much do the same thing. The main difference is one of semantics, and people generally fall into the habit of using one or the other, based on which syntax makes the most intuitive sense to them. However, one will usually tend to be more clear than another in a given situation.

Here's how Microsoft's VBScript documentation describes the Do loop (we added the bold emphasis).

Repeats a block of statements while a condition is True or until a condition becomes True.

As you can see, the distinction between While and Until is rather fuzzy. The easiest way to explain the difference is to modify our previous two examples replacing While with Until. You'll see that the consideration of whether to execute the loop at least once remains the same. However, the implementation is slightly different. Here's our first example, modified to use Until instead of While (DO_UNTIL.VBS).

Option Explicit

Dim boolLoopAgain
Dim lngLoopCount
Dim strResponse

boolLoopAgain = False
lngLoopCount = 0
Do
    boolLoopAgain = False
    lngLoopCount = lngLoopCount + 1

    strResponse = InputBox("What is the magic word?")
    If UCase(Trim(strResponse)) = "PLEASE" Then
        MsgBox "Correct! Congratulations!"
    Else
        If lngLoopCount < 5 Then
            MsgBox "Sorry, try again."
            boolLoopAgain = True
        Else
            MsgBox "Okay, the word we wanted was 'Please'."
        End If
    End If

Loop Until boolLoopAgain = False

It may look like the same code, but the difference is that we must test for a False value in our Until clause, whereas we tested for a True value in our While clause. When you read the line Loop While boolLoopAgain, does it make more sense than Loop Until boolLoopAgain = False? If the While syntax makes more sense to you, maybe we can fix that by changing the name of our variable (DO_UNTIL_BETTER_NAME.VBS).

Option Explicit

Dim boolStopLooping
Dim lngLoopCount
Dim strResponse

boolStopLooping = True
lngLoopCount = 0
Do
    boolStopLooping = True
    lngLoopCount = lngLoopCount + 1

    strResponse = InputBox("What is the magic word?")
    If UCase(Trim(strResponse)) = "PLEASE" Then
        MsgBox "Correct! Congratulations!"
    Else
        If lngLoopCount < 5 Then
            MsgBox "Sorry, try again."
            boolStopLooping = False
        Else
            MsgBox "Okay, the word we wanted was 'Please'."
        End If
    End If

Loop Until boolStopLooping = True

Does the Until syntax make a little more sense now? The point is you can use either While or Until to accomplish what you need to-it's just a matter of what makes more sense in a given situation. Let's look at our file reading example again, this time using Until instead of While (DO_UNTIL_READ_FILE.VBS).

Option Explicit

Dim objFSO
Dim objStream
Dim strText

Set objFSO = _
    WScript.CreateObject("Scripting.FileSystemObject")
Set objStream = objFSO.OpenTextFile("testfile.txt")
Set objFSO = Nothing

strText = ""
Do Until objStream.AtEndOfStream
    strText = strText & objStream.ReadLine & vbNewLine
Loop
Set objStream = Nothing

If strText <> "" Then
    MsgBox strText
Else
    MsgBox "The file is empty."
End If

The Until syntax might make this more clear. People sometimes have an easier time thinking in terms of positives, and the syntax Do While Not objStream.AtEndOfStream may be more or less clear to you than Do Until objStream.AtEndOfStream. It's up to you, though. VBScript doesn't care. And if you use good variable names, stick to straightforward logic, and make good use of indenting and white space, your fellow programmers most likely won't care either.

Before we move on to While…Wend, we need to mention the Exit Do statement. Like Exit For, you can use Exit Do to break out of a Do loop at any point. You can have as many Exit Do statements inside your loop as you like. Here's an example, yet another variation on our "magic word" example (DO_WHILE3.VBS).

Option Explicit

Dim boolLoopAgain
Dim lngLoopCount
Dim strResponse

boolLoopAgain = False
lngLoopCount = 0
Do
    boolLoopAgain = False
    lngLoopCount = lngLoopCount + 1

    strResponse = InputBox("What is the magic word?")
    If UCase(Trim(strResponse)) = "PLEASE" Then
        MsgBox "Correct! Congratulations!"
        Exit Do
    Else
        If lngLoopCount < 5 Then
            MsgBox "Sorry, try again."
            boolLoopAgain = True
        Else
            MsgBox "Okay, the word we wanted was 'Please'."
            Exit Do
        End If
    End If	
Loop While boolLoopAgain

Instead of setting boolLoopAgain to False, we just execute an Exit Do, which has the same effect in that we won't go around the loop again. When the Exit Do statement executes, the code jumps out of the loop, to the line of code immediately following the last line of the loop block (in our example, there is not any code after our loop, so the script ends). However, while this example illustrates the proper syntax for Exit Do, we have not necessarily made our magic word code any better by using it.

Remember the procedures and functions discussion in Chapter 4? When discussing the Exit Sub and Exit Function statements, we said that you should use them carefully and that there is usually a way to organize your code so that you do not have to use them. The potential problem with using Exit Sub and Exit Function is that the logic can become hard to follow because of the jumping out of the flow. The same principle applies to Exit Do.

If you compare the original magic word code to this new version, in the original we used the boolLoopAgain statement in conjunction with If conditions to control the loop. The logic flows from top to bottom in a linear fashion. Our new code with the Exit Do statements has lost that elegance and clarity.

A final note about Exit Do (and the other loop Exit statements as well): if you are working with nested loops, an Exit Do executed in the inner loop does not break out of the outer loop as well-only from the loop in which the Exit Do was executed.