Link to home
Create AccountLog in
Avatar of angelarmando
angelarmando

asked on

How to write a "mainframe" of "binary" style txt file in VBScript

Well folks, this is a continuation of a case that I'm having here and it deserves it own question for sure. I'm not an expert in Vbscript and I'm try to make one to process some txt files that come from a Healthcare Mainframe. Let me show you a basic example of what is my problem. I have attached 2 files. The first one is an example copy directly form the mainframe and the second is an output from my script above. I know that the problem relies on the way I'm reading or writing the file. Is there a way to manipulate this kind of text files by looking for something on it, write some stuff after that, and closing the file with the same format?
How can I make this work so the output is the same binary wise?
HINT: Put some watches on the different strlines and you will see what I'm talking about ;P

'Test Script
 
'reading tools
Const intForWriting = 2
Const intForReading = 1
 
'read the footers into variables
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set SecondobjFSO = CreateObject("Scripting.FileSystemObject")
 
 
'reading folder structure
 
strFolder = "C:\VBScriptstest\"
Set objFldr = objFSO.GetFolder(strFolder)
 
        
        x = 0
        For Each objFile In objFldr.Files
            strinputfile = strFolder & objFile.Name
              
                'First Read
                
                    Set objInputFile = objFSO.OpenTextFile(strinputfile, intForReading, False)
                    Set objlines = CreateObject("System.Collections.ArrayList")
                    While Not objInputFile.AtEndOfStream
                    strLine = objInputFile.ReadLine
                    objlines.Add strLine
                    Wend
                    objInputFile.Close
                    Set objInputFile = Nothing
                        strOutputFile = strFolder & "Processed\" & objFile.Name
                        If Not objFSO.FolderExists("C:\VBScriptstest\Processed") Then
                              Set newfolder = objFSO.CreateFolder("C:\VBScriptstest\Processed")
                        End If
                        Set objOutputFile = objFSO.CreateTextFile(strOutputFile, intForWriting, True)
                        For Each objLine In objlines
                               objOutputFile.WriteLine objLine
                        Next
                        objOutputFile.Close
                        Set objOutputFile = Nothing
                        
                        'Second Read
                        strinputfile = strOutputFile
                        Set objSecondInputFile = SecondobjFSO.OpenTextFile(strinputfile, intForReading, False)
                        Set Secondobjlines = CreateObject("System.Collections.ArrayList")
                        While Not objSecondInputFile.AtEndOfStream
                        SecondstrLine = objSecondInputFile.ReadLine
                        Secondobjlines.Add SecondstrLine
                        Wend
                        Secondobjlines.Add strFooter
                        objSecondInputFile.Close
                        Set objInputFile = Nothing
                        Set objSecondOutputFile = SecondobjFSO.CreateTextFile(strOutputFile, intForWriting, True)
                        For Each SecondobjLine In Secondobjlines
                               objSecondOutputFile.WriteLine SecondobjLine
                        Next
                        objSecondOutputFile.Close
                        Set objSecondOutputFile = Nothing
        Next
 
MsgBox "Done"

Open in new window

VBScriptstest.zip
Avatar of zoofan
zoofan
Flag of United States of America image

You need to be reading and writing the file in binary

an excellent example
http://nerds-central.blogspot.com/2007/01/ok-you-win-vbscript-read-binary-file.html


snippet below


zf



' This is a simple example of managing binary files in
' vbscript using the ADODB object
dim inStream,outStream
const adTypeText=2
const adTypeBinary=1
 
' We can create the scream object directly, it does not
' need to be built from a record set or anything like that
set inStream=WScript.CreateObject("ADODB.Stream")
 
' We call open on the stream with no arguments.  This makes
' the stream become an empty container which can be then
' used for what operations we want
inStream.Open
inStream.type=adTypeBinary
 
' Now we load a really BIG file.  This should show if the object
' reads the whole file at once or just attaches to the file on
' disk
' You must change this path to something on your computer!inStream.LoadFromFile("d:\Documents and Settings\aturner\Desktop\ScriptingDel\TestData\test.jpg")
 
' Copy the dat over to a stream for outputting
set outStream=WScript.CreateObject("ADODB.Stream")
outStream.Open
outStream.type=adTypeBinary
 
dim buff
buff=inStream.Read()
 
' Write out to a file
outStream.Write(buff)
' You must change this path to something on your computer!
outStream.SaveToFile("d:\Documents and Settings\aturner\Desktop\ScriptingDel\TestData\test_out.jpg")
 
outStream.Close()
inStream.Close()

Open in new window

Avatar of angelarmando
angelarmando

ASKER

Hey zoofan,
THANK YOU!
That works as a charm. Thanks! Now, with that reading method, do I loose all the capabilities to modify the txt file data like adding some lines depending on the content or deleting another ones? I'm asking because I have a version of what I want just using CreateObject("Scripting.FileSystemObject") but is giving me the "squares with text" kind of output...
Once you read the the binary stream if you want to modify it you would have to modify the stream(in binary) and then write the output.

It is not that you per say loose the ability but it changes what how you do it.

A few options:
You can read all the data in as a string, modify what you want and then convert the entire string to a binary stream for writing.

Convert what you want to add in to a binary stream, Read the data in as a binary stream adding your data to the stream where needed then write the combined stream to a new file.

 

a few examples:
http://www.motobit.com/tips/detpg_BinASP/
(see Function Stream_StringToBinary)

zf


Thanks zf,

Did just opened a new world of file manipulation for me ;P
But I'm afraid to ask...
I saw the examples and they look good. But I don't know when to start. Can you help me?
I just atached two files. The second one "_OUT" contains and example of what I'm trying to do. So if we dump the binary read to a string, how do we look for the specific line of tex to insert my additionals lines?
So if we are looking for something like this:
"XXXXXXXXXXXXXXXXXXXXXXXXX999999999999 X     XXXXXXXX XXXXXXXX 999"
to insert something like this afterwards:
"*************************************************************************************
*NEW                                                                                                        *
*TEXT                                                                                                       *
*BLOCK                                                                                                    *
*************************************************************************************
How can we do it?
' This is a simple example of managing binary files in
' vbscript using the ADODB object
dim inStream,outStream
const adTypeText=2
const adTypeBinary=1
 
' We can create the scream object directly, it does not
' need to be built from a record set or anything like that
set inStream=WScript.CreateObject("ADODB.Stream")
 
' We call open on the stream with no arguments.  This makes
' the stream become an empty container which can be then
' used for what operations we want
inStream.Open
inStream.type=adTypeBinary
 
' Now we load a really BIG file.  This should show if the object
' reads the whole file at once or just attaches to the file on
' disk
inStream.LoadFromFile("C:\VBScriptstest\AAUDTBGC_U510_20090429_0000.txt")
 
' Copy the dat over to a stream for outputting
set outStream=WScript.CreateObject("ADODB.Stream")
outStream.Open
outStream.type=adTypeBinary
 
dim buff
buff=inStream.Read()
 
' Write out to a file
outStream.Write(buff)
 
outStream.SaveToFile("C:\VBScriptstest\AAUDTBGC_U510_20090429_0000_OUT.txt")
 
outStream.Close()
inStream.Close()
 
msgbox "Done"

Open in new window

VBScriptstest.zip
Have the files, will look at them and get back with you.  Might be a little bit (at work:-) but will do.

Question is the string your looking for always the same or different? if different how to want to go about entering it? input box or manually edit the script?


zf

It's diferent....The script will look inside a buch of folders and every folder contains a collection of files. The "line to look" is different per folder but I had that take care of in code already by some variables. I just need some help inserting the Text block after a specific line an delete the same amount of lines after it. So if the text block is 5 lines, we have to delete 5 lines after the insert. I will be sure that those lines are empty ;P

Hey man sorry if this is a big task but any help is appreciated. I just want to learn for next time...

angelarmando
no problem sadly I enjoy it :-) ,  glad you mentioned you need to delete the equal amout you add.  will work with and get back to you.


zf
Quick question, you are looking to only replace the exact number of lines after the found line, correct?

not file size match in bytes (replacing line by line will change file size if bytes of new lines is less/more than bytes of old line)

zf
Right. The idea runs like this:

Open the input file
Read the file line by line
Close the input line (I guess)
Look for an specific line
Insert some specific lines of text from a variable right after
Delete the same amount of lines inserted just after the insertion
Create an outputfile in a subdirectory
Write the modified file in memory
Close it

What do you think?
ASKER CERTIFIED SOLUTION
Avatar of zoofan
zoofan
Flag of United States of America image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Dude! this looks awesome!!
Let me take a detailed look at it tonight..
I ran some tests and is doing his work...
I got like 10 questions already to unsderstand the way like you read the file...
Great stuff!
Talk to you soon.... :P
Perfect Answer!
Doing a line replace meant not actually having to do a binary read and write and made it much easier to deal with,  glad it works for you.


Will be happy to help you better understand what its doing.


zf
Hey zf,

I like you search system. Pretty clever. I'm getting some results! But of course I want to tweak it more. What if my strSearchLine repeats every often inside my file? Like some kind header? For example, in my case I'm looking for an specific line inside the file that contains "Page 1". So if you follow the logic, I'm looking to insert the text just before that line. So if my report (text file) contains 15 patients, I will have 15 "Page 1" lines. So the way is set up right now is to set the intLinetoAddAfter = intLineCount when it finds "Page 1". But only works in the last patient (Makes sense since intLinetoAddAfter will get overwritten).

So I have an idea:
How about setting that intLinetoAddAfter to a arrLinetoAddAfter that holds all the values of the lines to go and insert? How do I set that here?:

Do Until objInputFile.AtEndOfStream
                                ReDim Preserve arrFileLines(intLineCount + 1)
                                arrFileLines(intLineCount) = objInputFile.Readline
                                    If strSearchLine = arrFileLines(intLineCount) Then intLinetoAddAfter = intLineCount
                                intLineCount = intLineCount + 1
                            Loop

and here?:

For intLineLoop = 0 To intLineCount - 1
                                If intLineLoop = intLinetoAddAfter + 1 Then
                                    For intNewloop = LBound(arrLinesToAdd) To UBound(arrLinesToAdd)
                                        objOutputFile.Writeline arrLinesToAdd(intNewloop)
                                        intLineLoop = intLineLoop + 1
                                    Next
                                    intLineLoop = intLineLoop - 1
                                Else
                                    If intLineLoop <> intLineCount - 1 Then
                                        objOutputFile.Writeline arrFileLines(intLineLoop)
                                    Else
                                        objOutputFile.Write arrFileLines(intLineLoop)
                                    End If
                                End If
                            Next

Thanks a million zf ;P
So each file has more than one place to replace lines? like a file with 10 patients your search string appears 10 times and you need to do a replace on all ten?  trying to make sure I understand



zf
Ok. Imaging a report (txt) that is pretty user firendly.
It formatted like this:
TEST PAGE (we will ignored)
Patient 1Headers(First strSearchLine match will be there(PAGE 1 for example).
Details (With page numbers)
Totals (Footers)
Patient 1 Headers(Second strSearchLine match)
Details (With page numbers)
Totals (Footers)

and so and so....

Is working because if I got the last patient, the insertion is there. I think that we just have to have and array to store all those insertion matches (lines numbers) and then write and pause and insert when we hit one of them.. Does this makes sense?
yes,

working on it now, brb


zf
try this,

searches for each occurrence of the search string stores the line numbers as #,#,#,#

splits the return into an array and does a replace at each rebuilding the file.


zf
Option Explicit
Dim objFSO
Dim objInputFile
Dim objOutputFile
Dim arrFileLines()
Dim intLineCount
Dim strInputFIle
Dim strOutputFile
Dim strSearchLine
Dim intLinetoAddAfter
Dim intLineLoop
Dim intNewloop
Dim arrLineNums
Dim intAddLoop 
Dim intTempHoler
Dim bolThisline
Dim intLine
 
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0
 
'Edit to input/output filename
strInputFIle = "c:\in.txt"
strOutputFile = "c:\out.txt"
 
'Edit to EXACT line to search for including preceding spaces, case sensitive
strSearchLine = "  XXXXXXXXXXXXXXXXXXXXXXXXX999999999999 X     XXXXXXXX XXXXXXXX 999"
 
'Edit the number of elements in the array to the number of lines to replace 5 in this example (array is zero based)
Dim arrLinesToAdd(5)
 
'Edit add or remove lines here matching array elements number set above (array is zero based)
arrLinesToAdd(0) = "*************************************************************************************"
arrLinesToAdd(1) = "*                                        NEW                                        *"
arrLinesToAdd(2) = "*                                       BLOCK                                       *"
arrLinesToAdd(3) = "*                                      OF TEXT                                      *"
arrLinesToAdd(4) = "*************************************************************************************"
 
 
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInputFile = objFSO.OpenTextFile (strInputFIle,ForReading,False,TristateUseDefault)
 
intLineCount = 0
intLinetoAddAfter = ""
 
'Open and read the file line by line into arrFileLines test for our search line and store the line number
                Do Until objInputFile.AtEndOfStream
                        ReDim Preserve arrFileLines(intLineCount + 1)
                        arrFileLines(intLineCount) = objInputFile.Readline
                                If strSearchLine = arrFileLines(intLineCount) Then
                                intTempHoler = intLineCount + 1
                                 intLinetoAddAfter = intLinetoAddAfter & intTempHoler & ","
                                 End If
                        intLineCount = intLineCount + 1
                Loop
                objInputFile.Close
                Set objInputFile = Nothing
 arrLineNums = Split(intLinetoAddAfter,",")
 bolThisline = False
'If we found our line then write the new file
If intLinetoAddAfter <> vbNull Then
        Set objOutputFile = objFSO.OpenTextFile (strOutputFile,ForWriting,True,TristateUseDefault)
                For intLineLoop = 0 To intLineCount - 1
	                		For Each intLine In arrLineNums
	                			If cstr(intLine) = cstr(intLineLoop) Then 
	                				bolThisline = True
	                				Exit For
	                			Else
	                				bolThisline = False
	                			End if
	                		Next
                        If bolThisline = True Then
                        	bolThisline = False    
                                For intNewloop = LBound(arrLinesToAdd) To UBound(arrLinesToAdd)
                                        objOutputFile.WriteLine arrLinesToAdd(intNewloop)
                                        intLineLoop = intLineLoop + 1
                                Next
                                intLineLoop = intLineLoop - 1
                        Else
	                        If intLineLoop <> intLineCount - 1 Then
	                                objOutputFile.Writeline arrFileLines(intLineLoop)
	                        Else
	                                objOutputFile.Write arrFileLines(intLineLoop)
	                        End If
                        End If
                Next
                objOutputFile.Close
                WScript.Quit(0)
Else
        WScript.Quit(0)
End If

Open in new window

Wow.... you just make it look so easy. Let me try it out! I'm sure it will work ;P
BTW, I was working in a solution too... I will implement yours of course but I will post mine so you can give me your opinion. It will be an very valuable to me :)
lol, thanks but I'm a novice at best.  

Learning my way thru it, come here to get ideas and problems to solve from others.

Its an unlimited supply of real world problems that aren't my headache :-)  and most I would never come across in my environment, kinda like my leaning aid...


will gladly look at what you have as well.


zf
Alright my friend! I got a posible solution using the arrLinetoAddAfter. (I still gona use yours ;P)
Please grade this:
To read and Identify the lines:

Set objInputFile = objFSO.OpenTextFile(strInputFIle, ForReading, False, TristateUseDefault)
                    
                    'Reset Counters
                    intLineCount = 0
                    Erase arrLinetoAddAfter()
                    ReDim arrLinetoAddAfter(0)
                    intAfterCounter = 0
                    
                    'Read the file line by line into arrFileLines test for our search line and store the line number
                            Do Until objInputFile.AtEndOfStream
                                ReDim Preserve arrFileLines(intLineCount + 1)
                                arrFileLines(intLineCount) = objInputFile.Readline
                                    If strSearchLine = arrFileLines(intLineCount) Then
                                    ReDim Preserve arrLinetoAddAfter(intAfterCounter)
                                    arrLinetoAddAfter(intAfterCounter) = intLineCount
                                    intAfterCounter = intAfterCounter + 1
                                    End If
                                intLineCount = intLineCount + 1
                            Loop
                            objInputFile.Close
                            Set objInputFile = Nothing
'If we found our lines then write the new file
                    If arrLinetoAddAfter(0) <> 0 Then
Set objOutputFile = objFSO.OpenTextFile(strOutputFile, ForWriting, True, TristateUseDefault)
                            intPosition = 0
                            For intLineLoop = 0 To intLineCount - 1
                                If intLineLoop = arrLinetoAddAfter(intPosition) Then
                                    If intPosition < UBound(arrLinetoAddAfter) Then
                                           intPosition = intPosition + 1
                                    End If
                                    For intNewloop = LBound(arrLinesToAdd) To UBound(arrLinesToAdd)
                                        objOutputFile.Writeline arrLinesToAdd(intNewloop)
                                        intLineLoop = intLineLoop + 1
                                    Next
                                    intLineLoop = intLineLoop - 1
                                Else
                                    If intLineLoop <> intLineCount - 1 Then
                                        objOutputFile.Writeline arrFileLines(intLineLoop)
                                    Else
                                        objOutputFile.Write arrFileLines(intLineLoop)
                                    End If
                                End If
                            Next
                            objOutputFile.Close
                    End If

Open in new window

Great job!!

only found two things noted both below in snippet.

very well done!  more than one way to skin a cat!!  and btw your code is more efficient as you removed the need for an additional for/next loop that I had in mine.


Use yours!!!!

zf

Set objInputFile = objFSO.OpenTextFile(strInputFIle, ForReading, False, TristateUseDefault)
                    
                    'Reset Counters
                    intLineCount = 0
                    'Erase arrLinetoAddAfter() 											'Removed this line dont need it as we reDim on next line(redim without preserve erases the array)
                    ReDim arrLinetoAddAfter(0)
                    intAfterCounter = 0
                    
                    'Read the file line by line into arrFileLines test for our search line and store the line number
                            Do Until objInputFile.AtEndOfStream
                                ReDim Preserve arrFileLines(intLineCount + 1)
                                arrFileLines(intLineCount) = objInputFile.Readline
                                    If strSearchLine = arrFileLines(intLineCount) Then
                                    ReDim Preserve arrLinetoAddAfter(intAfterCounter)
                                    arrLinetoAddAfter(intAfterCounter) = intLineCount + 1 	'need to add 1 to this to get the correct line number (we want to start our replace on the line after we find our string)
                                    intAfterCounter = intAfterCounter + 1
                                    End If
                                intLineCount = intLineCount + 1
                            Loop
                            objInputFile.Close
                            Set objInputFile = Nothing
'If we found our lines then write the new file
                    If arrLinetoAddAfter(0) <> 0 Then
Set objOutputFile = objFSO.OpenTextFile(strOutputFile, ForWriting, True, TristateUseDefault)
                            intPosition = 0
                            For intLineLoop = 0 To intLineCount - 1
                                If intLineLoop = arrLinetoAddAfter(intPosition) Then
                                    If intPosition < UBound(arrLinetoAddAfter) Then
                                           intPosition = intPosition + 1
                                    End If
                                    For intNewloop = LBound(arrLinesToAdd) To UBound(arrLinesToAdd)
                                        objOutputFile.Writeline arrLinesToAdd(intNewloop)
                                        intLineLoop = intLineLoop + 1
                                    Next
                                    intLineLoop = intLineLoop - 1
                                Else
                                    If intLineLoop <> intLineCount - 1 Then
                                        objOutputFile.Writeline arrFileLines(intLineLoop)
                                    Else
                                        objOutputFile.Write arrFileLines(intLineLoop)
                                    End If
                                End If
                            Next
                            objOutputFile.Close
                    End If

Open in new window

Is a nice change of pace to see one trying to not only solve their problems but learn from them, many want only for made-to order solutions and move on.


Thank you for reminding me why I love this site.....


zf
Just in case that cat has a tiny bit of hair left :-)  you inspired me to rewrite the orginal

see below,  now were cooking!!!!! with 45% less code



zf
Option Explicit
Dim objFSO
Dim objInputFile
Dim objOutputFile
Dim strFileLine
Dim strInputFIle
Dim strOutputFile
Dim strSearchLine
Dim intNewloop
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0
 
'Edit to input/output filename
strInputFIle = "M:\VBScriptstest\in.txt"
strOutputFile = "M:\VBScriptstest\out.txt"
 
'Edit to EXACT line to search for including preceding spaces, case sensitive
strSearchLine = "  XXXXXXXXXXXXXXXXXXXXXXXXX999999999999 X     XXXXXXXX XXXXXXXX 999"
 
'Edit the number of elements in the array to the number of lines to replace 5 in this example (array is zero based)
Dim arrLinesToAdd(5)
 
'Edit add or remove lines here matching array elements number set above (array is zero based)
arrLinesToAdd(0) = "*************************************************************************************"
arrLinesToAdd(1) = "*                                        NEW                                        *"
arrLinesToAdd(2) = "*                                       BLOCK                                       *"
arrLinesToAdd(3) = "*                                      OF TEXT                                      *"
arrLinesToAdd(4) = "*************************************************************************************"
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInputFile = objFSO.OpenTextFile (strInputFIle,ForReading,False,TristateUseDefault)        
Set objOutputFile = objFSO.OpenTextFile (strOutputFile,ForWriting,True,TristateUseDefault)
 
                Do Until objInputFile.AtEndOfStream
                        	strFileLine = objInputFile.Readline
                                If strSearchLine = strFileLine Then
                                	objOutputFile.Writeline strFileLine
						For intNewloop = LBound(arrLinesToAdd) To UBound(arrLinesToAdd)
                                        objOutputFile.WriteLine arrLinesToAdd(intNewloop)
                                         strFileLine = objInputFile.Readline
                                	Next
                                	     strFileLine = objInputFile.Readline
                                 End If
	                        If objInputFile.AtEndOfStream Then
	                        	objOutputFile.Write strFileLine
	                        Else
	                        	objOutputFile.Writeline strFileLine
	                        End If
                Loop
                objInputFile.Close
                Set objInputFile = Nothing
                objOutputFile.Close
                Set objOutputFile = Nothing
 WScript.Quit(0)

Open in new window

Nicely done. I'm glad that you like it. Hey, I'm here to learn dude from all of you. I will keep you in my contacts.


Thanks for everything!

Now, I will keep modifying it to make a final version.

BTW, I'm learning VB.NET 2008 too so this site might give me some ideas ;P
(My VB6 is a little rusty!)