Hey, Scripting Guy! How can I parse a tab-delimited file, and then save the resulting information as a comma-separated values file?
— DM
Hey, DM. You know, yesterday morning the Scripting Guy who writes this column had to scrape the ice off his windshield; today itβs much warmer β 44 at last report β although it is raining. Of course, you know what that means: the Seattle-area baseball season is in full swing!
We mention that simply because it provides a sneaky way for us to also mention that the Script Son took the mound for his high school team yesterday afternoon. The results? Five shutout innings, one hit (on a checked-swing), no walks, eight strikeouts. Not bad for his first outing of the season, although itβs a shame that he gave up that one hit; after all, thatβs one more hit than the Scripting Dad gave up during his entire high school career.
|
Note. Whatβs that? How many times did the Scripting Dad actually pitch during his high school career? Well, thatβs hard to say; after all, that was a long time ago, and pitching records from that era are spotty at best. Off the top of our heads, weβd have to say the number of times he pitched was definitely greater than β or, at least, equal to β zero. |
But, hey, this is supposed to be about the Scripting Son. Letβs let him have his moment and not concern ourselves with the Scripting Dad.
At any rate, itβs always a treat to watch your son mow down the opposition. He blew his fast ball right by several batters, and completely froze several more with his curve.
Although, come to think of it, maybe the curveball didnβt freeze the hitters as much as the weather did. The Scripting Dad, for example, was wearing long underwear, a long-sleeved shirt, a hooded sweatshirt, a coat, and a pair of gloves. If that doesnβt scream βbaseball seasonβ we donβt know what does.
Of course, in years past the start of baseball season meant that the Scripting Guy who writes this column disappears and isnβt heard from again until August; thatβs because he finds baseball far more interesting than work. (Hard to believe, but he does.) Right now, however, he has to admit that he finds work far warmer than he finds baseball. Which means that, rather than disappearing, heβs going to sit in his nice warm office and see if he can figure out how to parse a tab-delimited file, and then save the resulting information as a comma-separated values (CSV) file.
At least until itβs time to leave work early and head for the next game.
To begin with, DM has a text file that looks something like this, with fields separated by tabs:
Cre Rec Name=Jack Address=5 XYZ Drive Phone=555-4567 Cre Rec Name=Jill Address=7 XYZ Drive Phone=555-6547 Cre Rec Name=Jake Address=9 XYZ Drive Phone=555-9876
And sure, that is a very nice text file. But itβs nowhere near as nice as this CSV file, which is the file DM would like to have:
Name,Address,Phone “Jack”,”5 XYZ Drive”,”555-4567″ “Jill”,”7 XYZ Drive”,”555-6547″ “Jake”,”9 XYZ Drive”,”555-9876″
The question for today is this: how can DM get from point A (the tab-delimited file) to point B (the CSV file)? Hereβs how:
Const ForReading = 1Set objFSO = CreateObject(“Scripting.FileSystemObject”) Set objFile = objFSO.OpenTextFile(“C:\Scripts\Test.txt”, ForReading)
Do Until objFile.AtEndOfStream strLine = objFile.ReadLine arrFields = Split(strLine, vbTab) arrName = Split(arrFields(1), “=”) strName = arrName(1) arrAddress = Split(arrFields(2), “=”) strAddress = arrAddress(1) arrPhone = Split(arrFields(3), “=”) strPhone = arrPhone(1) strNewContent = strNewContent & Chr(34) & strName & Chr(34) & “,” & Chr(34) & strAddress & Chr(34) & _ “,” & Chr(34) & strPhone & Chr(34) & vbCrLf Loop
objFile.Close
Set objFile = objFSO.CreateTextFile(“C:\Scripts\Test.csv”)
objFile.WriteLine “Name,Address,Phone” objFile.Write strNewContent
objFile.Close
As you can see, we start out by defining a constant named ForReading and setting the value to 1; weβll use this constant when we set out to open our tab-delimited file. We then use these two lines of code to create an instance of the Scripting.FileSystemObject and to open the file C:\Scripts\Test.txt:
Set objFSO = CreateObject(“Scripting.FileSystemObject”) Set objFile = objFSO.OpenTextFile(“C:\Scripts\Test.txt”, ForReading)
So now what do we do?
Oh, right: thatβs our job to figure that out, isnβt it? OK, well, hereβs one thing we can do: we can set up a Do Until loop that reads the tab-delimited file line-by-line. In other words, a Do Until loop that runs until the fileβs AtEndOfStream property is True:
Do Until objFile.AtEndOfStream
Inside that loop we use this line of code to read the first line in the file and store it in a variable named strLine:
strLine = objFile.ReadLine
What does that mean? That means that strLine is equal to this:
Cre RecName=JackAddress=5 XYZ DrivePhone=555-4567
Of course, that doesnβt do us much good; somehow we need to tease out the individual values for Name, Address, and Phone. Is that too much to ask for? Of course it is. But letβs give it a try anyway.
To begin with, we use the Split function within the following line of code to split the value of strLine into an array:
arrFields = Split(strLine, vbTab)
By splitting the value on the tab character (using the VBScript constant vbTab) we end up with an array named arrFields that consists of the following items:
| β’ |
Cre Rec |
| β’ |
Name=Jack |
| β’ |
Address=5 XYZ Drive |
| β’ |
Phone=555-4567 |
OK, now weβre getting somewhere. As you can see, the userβs name (Jack) is part of the second item in the array. (And, remember, because the first item in an array has an index number 0 that means the second item in the array has an index number of 1). The only problem, of course, is that the name is prefaced with Name=. So why donβt we just get rid of that prefix:
arrName = Split(arrFields(1), “=”) strName = arrName(1)
See what weβre doing here? In the first line, weβre again using the Split function to create an array; this time weβre splitting the value of array item 1 (Name=Jack) on the equals sign (=). What does that give us? That gives us a little two-item array named arrName, an array that looks like this:
| β’ |
Name |
| β’ |
Jack |
Well, what do you know: the second item in our mini-array (index number 1) just happens to be the user name. That means we can grab the name simply by assigning the value of array item 1 to the variable strName, which is what we do in our second line of code.
Get the idea? We then repeat the process with index numbers 2 (address) and 3 (phone number), like so:
arrAddress = Split(arrFields(2), “=”) strAddress = arrAddress(1) arrPhone = Split(arrFields(3), “=”) strPhone = arrPhone(1)
We now have three variables β strName, strAddress, and strPhone β that contain information parsed from the text file. That means that our next step is to begin constructing the CSV file. Thatβs what this monstrosity is for:
strNewContent = strNewContent & Chr(34) & strName & Chr(34) & “,” & Chr(34) & strAddress & Chr(34) & _
“,” & Chr(34) & strPhone & Chr(34) & vbCrLf
Donβt let this line of code deter you: its bark is far worse than its bite. All weβre doing here is assigning a value to a variable named strNewContent. What value are we assigning to strNewContent? Well, weβre assigning the existing value of the variable plus the following:
| β’ |
Double quotes (thatβs what the Chr(34) is for). |
| β’ |
The value of the variable strName. |
| β’ |
More double quotes. |
| β’ |
A comma (β,β). |
| β’ |
Double quotes. |
| β’ |
The value of the variable strAddress. |
| β’ |
Double quotes. |
| β’ |
Another comma. |
| β’ |
Double quotes. |
| β’ |
The value of the variable strPhone. |
| β’ |
Double quotes. |
| β’ |
A carriage return-linefeed character (vbCrLf). |
Why such a complicated construction? What weβre trying to do is guard against any items that might include a comma. For example, suppose a user has the name Ken Myer, Jr. If we simply write that name as-is to a CSV file, the CSV file will assume that we have two values: Ken Myer and Jr. Thatβs because the comma is used as the delimiter. In turn, a simple thing like that can cause our CSV file (and any scripts/applications that need to read that file) to go completely haywire. To prevent that from happening we enclose each value in double quotes, like so:
“Ken Myer, Jr.”
In a CSV file, items enclosed in double quotes are treated as a single value, even if the value includes commas.
See? Weβre doing this for your own good!
After that we then loop around and repeat the process with the next line in the tab-delimited file.
Once weβve read and processed the entire file weβre ready to create the CVS file. To do that we first close the file Test.txt, then use this line of code to create a new text file named C:\Scripts\Test.csv:
Set objFile = objFSO.CreateTextFile(“C:\Scripts\Test.csv”)
Because DM needed a header line the first thing we do to our new file is use this line of code to write a header to the thing:
objFile.WriteLine “Name,Address,Phone”
And then all we have to do is use the Write method to write the value of strNewContent to the file. (And then use the Close method to close Test.csv.) At that point we β just like the hitters that had to go up against the Scripting Son β are finished.
Hope that helps. DM. Itβs a little complicated, but not if you take a deep breath and work your way through it. And we know that a lot of people are faced with similar tasks. We hope this helps all of you as well.
Meanwhile, we β whatβs that? Oh, right: you noticed that small patch of blue sky off in the distance. If the weatherβs improving, does that mean that baseball season is already over?
No, far from it: if the weather is improving that simply means that the Scripting Son doesnβt have a game tonight. But donβt worry: the forecast for tomorrowβs game includes a temperature of 49 degrees and rain. Now thatβs baseball weather.
0 comments