Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I number the lines in a text file?

-- AS

SpacerHey, Scripting Guy! AnswerScript Center

Hey, AS. You know, after being on vacation for three weeks the Scripting Guy who writes this column has a lot of catching up to do. In turn, that means that you have a lot of catching up to do. For example, did you know that the Seattle area experienced a huge power outage shortly before Christmas? Well, it’s true; in fact, the Scripting Guy who writes this column went six days before power was restored to his house. Did desperation set in for the Scripting Family? Let’s put it this way: they were just about to draw lots to determine which family member would be killed and eaten when the power miraculously came back on.

OK, to be honest it wasn’t quite that bad. For example, the Scripting family room stayed at a (relatively) balmy 50 degrees Fahrenheit, which meant that at least one portion of the house was (relatively) inhabitable. What about the rest of the house? Well, after a couple of days the Scripting Guy who writes this column noticed that the ice in the freezer was starting to melt. Therefore, he dumped all the ice into the sink. Two days later, the ice was still there; it had barely melted at all. In other words, it was warmer in the freezer than it was in the rest of the house.

Which is usually not a very good sign.

Of course, one advantage to not having any electricity was the fact that it gave the Scripting Guy who writes this column lots of time to just sit and think. And what did he think about? Why, how to write a script that would number all the lines in a text file, of course.

OK, that’s not really true, either; instead, he spent most of his time thinking about the best way, if worse came to worse, to barbecue the Scripting Son. (Editor’s Note: Is anyone else getting a little grossed out at this point?) The Scripting Guy who writes this column just said that he thought about writing a script that would number all the lines in a text file because it made a nice lead-in to this, a script which actually does number all the lines in a text file:

Const ForReading = 1
Const ForWriting = 2

i = 1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    strContents = strContents & i & "   " & strLine & vbCrLf
    i = i + 1
Loop

objFile.Close

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)
objFile.Write strContents
objFile.Close

Let’s see if we can figure out how this baby works. We begin by defining a pair of constants – ForReading and ForWriting – that we’ll use when working with the text file. We then set the value of a counter variable named i to 1; we’ll use this variable to keep track of line numbers.

Got all that? Good. Now we’re ready to start. To begin with, we use these two lines of code to create an instance of the Scripting.FileSystemObject and then open the file C:\Scripts\Test.txt:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

You might have noticed that we opened the file for reading (hence the use of the constant ForReading). As most of you know by now, that’s due to a quirk in the FileSystemObject: you can open a file for reading or you can open a file for writing, but you can’t do both operations (reading and writing) at the same time. Because of that we need to read the file into memory, add the line numbers to this “virtual” copy of the file, then reopen the file for writing and replace the existing lines with the newly-numbered lines.

To that end we first set up a Do Until loop that runs until we reach the end of the file (that is, until the file’s AtEndOfStream property is True). Inside the loop we use the ReadLine method to read the first line of the file, storing that value in a variable named strLine. That brings us to this line of code:

strContents = strContents & i & "   " & strLine & vbCrLf

This is the code we use to construct a new file in memory, a file in which each line is numbered. As you can see, what we’re doing here is assigning a value to a variable named strContents. What value are we assigning to the variable? We’re assigning it the following:

The current value of strContents (which, the first time through the loop, is nothing).

The value of the counter variable i (which, the first through the loop, is 1).

Three blank spaces.

The value of the variable strLine (which, the first time through the loop, is equal to the first line in the text file).

A carriage return-linefeed (represented by the VBScript constant vbCrLf).

For example, suppose our text file looks like this:

This is line 1.
This is line 2.
This is line 3.
This is line 4.

When we run through the loop the first time, our “equation” will look like this:

[nothing]
+                            1
+         [three blank spaces]
+ [a carriage return-linefeed]

In turn, that means strContents will be equal to this:

1   This line 1.

We then increment the value of i by 1, loop around and repeat the process with the second line of the text file. At the end of this second loop strContents will be equal to this:

1   This line 1.
2   This line 2.

And so on. When we’re all done strContents will be equal to this:

1   This line 1.
2   This line 2.
3   This line 3.
4   This line 4.

Which, in fortuitous fashion, just happens to be what we want our text file to look like.

In a perfect world we’d now simply replace the old contents of Test.txt with these new and improved contents. As the Scripting Guy who writes this column can attest, however, this isn’t always a perfect world. That’s as true of the FileSystemObject as it is the power grid. We want to simply replace the contents of Test.txt just like we want to be able to go to bed at night without having to wear long underwear, sweatpants, a sweatshirt, and a knitted ski cap. But, as the Rolling Stones warned us years ago, you can’t always get what you want.

Therefore, we need to take a somewhat more circuitous path. (Hey, how about that? We used both fortuitous and circuitous in the same column. We’ll try to fit in gratuitous if at all possible.) For starters, we use the Close method to close our text file:

objFile.Close

We then turn right around and reopen the file, this time for writing:

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForWriting)

As soon as the file is reopened we can then call the Write method and write the value of strContents to Test.txt. After we call the Close method to close the file Test.txt will look like this:

1   This line 1.
2   This line 2.
3   This line 3.
4   This line 4.

And there you have it.

And, to be honest, there truly was one nice thing about the blackout: it gave the Scripting Guy who writes this column an opportunity to prove that he was every bit as rugged and resourceful as people who lived in the days before electricity and modern conveniences. Of course, that’s assuming that those people just hopped in the car and drove to a nearby restaurant any time they got hungry. (Most areas around Seattle had power restored within two days.) And that they were able to while away those long, cold winter nights by attending a college basketball game; the University of Washington lost power only briefly, if at all. And that, instead of huddling together during the day in order to keep warm, they went Christmas shopping at the mall.

But that seems reasonable enough to assume.