How can we decrease code execution time in VBScript - vbscript

I have the below code to replace NUL characters in a text file. This code is working as per my requirement for smaller files but the problem is when the file size is increasing it is taking more time. I have a file which consists of more than 200,000 lines consists of 160MB+ size. I have executed my code for this file and I waited for more than 2 hours still the code executing.
Const ForReading = 1
Const ForWriting = 2
Const TriStateUseDefault = -2
If (WScript.Arguments.Count > 0) Then
sInfile = WScript.Arguments(0)
Else
WScript.Echo "No filename specified."
WScript.Quit
End If
If (WScript.Arguments.Count > 1) Then
sOutfile = WScript.Arguments(1)
Else
sOutfile = sInfile
End If
'Get the text file from cmd file
sData = ""
FinalData = ""
sInfile = WScript.Arguments(1)
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set re = New RegExp
re.Pattern = "\x00.*"
re.Global = True
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
Do Until f.AtEndOfStream
sData = Replace(f.ReadLine, vbCrLf, "")
FinalData = FinalData + re.Replace(sData, "") + vbCrLf
Loop
f.Close
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
oOutfile.Write(FinalData)
oOutfile.Close
Set oOutfile = Nothing
Set oFS = Nothing
WScript.Quit
Is there any way to optimize the code to execute in less interval of time.
EDIT 1:
Updated Code:
Const ForReading = 1
Const ForWriting = 2
Const TriStateUseDefault = -2
If (WScript.Arguments.Count > 0) Then
sInfile = WScript.Arguments(0)
Else
WScript.Echo "No filename specified."
WScript.Quit
End If
If (WScript.Arguments.Count > 1) Then
sOutfile = WScript.Arguments(1)
Else
sOutfile = sInfile
End If
'Get the text file from cmd file
sData = ""
FinalData = ""
sInfile = WScript.Arguments(1)
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set re = New RegExp
re.Pattern = "\x00.*"
re.Global = True
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
Do Until f.AtEndOfStream
sData = Replace(f.ReadAll, vbCrLf, "")
FinalData = FinalData + re.Replace(sData, "") + vbCrLf
Loop
f.Close
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
oOutfile.Write(FinalData)
oOutfile.Close
Set oOutfile = Nothing
Set oFS = Nothing
WScript.Quit

Do not use ReadAll for large files. Reading large files into memory might exhaust the available RAM on your computer, so that it will come grinding to a halt because it starts swapping.
Also avoid concatenating strings in a loop, because the operation is slow.
Change this:
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
Do Until f.AtEndOfStream
sData = Replace(f.ReadLine, vbCrLf, "")
FinalData = FinalData + re.Replace(sData, "") + vbCrLf
Loop
f.Close
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
oOutfile.Write(FinalData)
oOutfile.Close
to this:
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
Do Until f.AtEndOfStream
oOutFile.WriteLine re.Replace(f.ReadLine, "")
Loop
f.Close
oOutfile.Close
Same code with string operations instead of a regular expression replacement:
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
Do Until f.AtEndOfStream
line = f.ReadLine
pos = InStr(line, Chr(0))
If pos > 0 Then line = Left(line, pos-1)
oOutFile.WriteLine line
Loop
f.Close
oOutfile.Close

I know it's not up to date, but it might be useful to someone.
I tried another approach that takes about 5 seconds! :)
It seems that scripting engine (wscript) or FileSystemObject has a problem loading 160 MB at a time (by .ReadAll method).
So I tried to load all data (into the Dictionary) line by line via .ReadLine, process it and then save it to the output file at once.
Appendix:
- I added the option to create a test file - if you specify "CreateData" as the second argument:wscript util.vbs "C:\Temp\SampleData.txt" CreateData
- You do not need to remove CR + LF from a string that returns .ReadLine. They are already skipped.
- Sometimes it is good to test .AtEndOfStream before .ReadAll method, because if the file will be empty, the method will cause run-time error.
Dim mode, sInFile, sOutFile
If (WScript.Arguments.Count > 0) Then
sInfile = WScript.Arguments(0)
Else
WScript.Echo "No filename specified."
WScript.Quit
End If
If (WScript.Arguments.Count > 1) Then
If StrComp(WScript.Arguments(1), "CreateData", 1) = 0 then
mode = "CreateData"
sOutfile = sInFile
Else
mode = "processing"
sOutfile = WScript.Arguments(1)
End If
Else
mode = "processing"
sOutfile = sInfile
End If
Set oFSO = CreateObject("Scripting.FileSystemObject")
if mode = "CreateData" then
Call CreateDataFile(sInfile, 160) '160 = approx. 160 MB'
Wscript.Quit
end if
Dim dictData, i, sLine, tim
tim = Timer()
'Load data
set dictData = CreateObject("Scripting.Dictionary")
Set f = oFSO.OpenTextFile(sInfile, 1, False, -1)
do while not f.AtEndOfStream
dictData.Add dictData.Count, f.ReadLine()
loop
f.Close
'Process data
for each i in dictData
sLine = dictData(i)
dictData(i) = Replace(sLine, Chr(0), "")
next
'Save processed data
sFinalData = Join(dictData.Items, vbCrLf)
Set oOutfile = oFSO.OpenTextFile(sOutfile, 2, True, -1)
oOutFile.Write sFinalData
oOutfile.Close
'Message
WScript.Echo "Data processed (" & (Timer() - tim) & " sec)"
'-------------------------------------------------------------------------------
sub CreateDataFile(ByVal sFilePath, ByVal nSizeInMB)
'-------------------------------------------------------------------------------
Dim sLine, arrData, i, iMax, sData, tim
rem tim = Timer()
sLine = String(255, "A") & Chr(0) & String(254, "B")
iMax = CLng((nSizeInMB*1024*1024)/(Len(sLine)*2)) 'Unicode chars take 2 bytes
ReDim arrData(iMax)
for i = 0 to iMax
arrData(i) = sLine
next
sData = Join(arrData, vbCrLf)
set oFile = oFSO.CreateTextFile(sFilePath, True, True)
oFile.Write sData
oFile.Close
rem WScript.Echo "Data created (" & (Timer() - tim) & " sec)"
end sub

Related

VB Script to Copy lines from text file containing Keyword

VB Script to Copy lines from text file containing Keyword "Failed". I have a script I am editing that has, so far only been able to copy only instance if the failed lines. In this case, the file only has 2 lines that need to be logged but the are over 12,000 total successful lines. Example below.
1111 ,1 ,xxxx ,xxxx ,1 ,10/01/2022 ,07/29/2022 ,1111 ,200 , , , , , ,Failed, , An error occurred while updating the entries. See the inner exception for details ,
2222 ,2 ,xxxxo ,Kxxxx ,2 ,10/01/2022 ,07/29/2022 ,2222 ,0 , , , , , ,Successfully imported, , ,
33333 ,3 ,Uxxxx ,Jxxxx ,1 ,07/25/2022 ,07/29/2022 ,3333 ,200 , , , , , ,Failed, , An error occurred while updating the entries. See the inner exception for details ,
The current script creates blank lines for the successful lines and only logs the 1 failed line. Trying to get this to all lines containing Failed.
Here is what I am working with.
Option Explicit
Dim StdIn: Set StdIn = WScript.StdIn
Dim StdOut: Set StdOut = WScript
Main()
Sub Main()
Dim objFSO, filepath, objInputFile, tmpStr, ForWriting, ForReading, count, text, objOutputFile, index, LOGFILE, foundFirstMatch
Set objFSO = CreateObject("Scripting.FileSystemObject")
LOGFILE = "c:\New folder\Errorlog.csv"
ForReading = 1
ForWriting = 2
Set objInputFile = objFSO.OpenTextFile(LOGFILE, ForReading, False)
text="Fail"
foundFirstMatch = false
Do until objInputFile.AtEndOfStream
tmpStr = objInputFile.ReadLine
If foundStrMatch(tmpStr)=true Then
If foundFirstMatch = false Then
index = getIndex(tmpStr)
foundFirstMatch = true
text = text & vbCrLf & textSubstitution(tmpStr,index,"true")
End If
If index = getIndex(tmpStr) Then
text = text & vbCrLf & textSubstitution(tmpStr,index,"false")
ElseIf index < getIndex(tmpStr) Then
index = getIndex(tmpStr)
text = text & vbCrLf & textSubstitution(tmpStr,index,"true")
End If
Else
text = text & vbCrLf & textSubstitution(tmpStr,index,"false")
End If
Loop
Set objOutputFile = objFSO.CreateTextFile("C:\New folder\Log2.txt", ForWriting, true)
objOutputFile.Write(text)
End Sub
Function textSubstitution(tmpStr,index,foundMatch)
'Dim strToAdd
'strToAdd = "<tr><td>Beginning_of_CF5.0_Features_TC" & CStr(index) & "</td></tr>"
'If foundMatch = "false" Then
'textSubstitution = tmpStr
If foundMatch = "true" Then
textSubstitution = tmpStr
End If
End Function
Function getIndex(tmpStr)
Dim substrToFind, charAtPos, char1, char2
substrToFind = "Failed"
charAtPos = len(substrToFind) + 1
char1 = Mid(tmpStr, charAtPos, 1)
char2 = Mid(tmpStr, charAtPos+1, 1)
'If IsNumeric(char2) Then
'getIndex = CInt(char1 & char2)
'Else
'getIndex = CInt(char1)
'End If
End Function
Function foundStrMatch(tmpStr)
Dim substrToFind
substrToFind = "Failed"
If InStr(tmpStr, substrToFind) > 0 Then
foundStrMatch = true
Else
foundStrMatch = false
End If
End Function
You should write matched lines as you find them, like this:
Set objFSO = CreateObject("Scripting.FileSystemObject")
LOGFILE = "c:\New folder\Errorlog.csv"
OUTFILE = "C:\New folder\Log2.txt"
ForReading = 1
ForWriting = 2
Set objInputFile = objFSO.OpenTextFile(LOGFILE, ForReading, False)
Set objOutputFile = objFSO.CreateTextFile(OUTFILE, ForWriting, true)
text="Fail"
Do until objInputFile.AtEndOfStream
tmpStr = objInputFile.ReadLine
If InStr(tmpStr,text)>0 Then objOutputFile.WriteLine(tmpStr)
Loop
objInputFile.Close
objOutputFile.Close

VBscript - Hot to Write to a specific blank line?

I need to insert a strText to line 14 in a template.txt file. Line 14 will always be blank before writing (sort of like appending I guess).
What I really need is to copy line 21 to line 14. Not sure what is the easier method to achieve this?
Here is what I have so far but not working. Below code is the template.txt.
Dim objFSO : Set objFSO = CreateObject("Scripting.FileSystemObject")
DIM Infile : Infile = "C:\template.txt"
Set tempFile = objFSO.OpenTextFile (Infile)
strText = tempFile.ReadAll
tempFile.Close
strNewText = Replace(strText, "","Channel_LandWaterMask = 3.0")
Set tempFile = objFSO.OpenTextFile (Infile, 2)
tempfile.Writeline 14, strNewText ‘(How would I write this???)
tempFile.Close
Here is the template.txt:
; Resample INF script for
; Section_YY XX_ZZZZ
[Source]
Type = MultiSource
NumberOfSources = 2
[Source1]
Type = GeoTIFF
Layer = Imagery
SourceDir = "S:\XX\Section_YY\Images"
SourceFile = "XX_ZZZZ_CC.tif"
Variation = DAY
[Source2]
Type = GeoTIFF
Layer = Imagery
SourceDir = "S:\XX\Section_YY\Images"
SourceFile = "XX_ZZZZ_LM.tif"
Variation = Night
Channel_LandWaterMask = 3.0
[Source3]
Type = GeoTIFF
Layer = None
SourceDir = "S:\XX\Section_YY\Images"
SourceFile = "XX_ZZZZ_WM.tif"
SamplingMethod = Gaussian
[Destination]
DestDir = "S:\2_Output\Section_YY"
DestBaseFileName = "XX_ZZZZ"
DestFileType = BGL
LOD = Auto
UseSourceDimensions = 1
CompressionQuality = 85
Take a look at the below example:
sContent = ReadTextFile("C:\template.txt", 0)
aContent = Split(sContent, vbCrLf)
aContent(13) = aContent(20) & vbCrLf & aContent(13)
sContent = Join(aContent, vbCrLf)
WriteTextFile sContent, "C:\template.txt", 0
Function ReadTextFile(sPath, lFormat)
' lFormat -2 - System default, -1 - Unicode, 0 - ASCII
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, lFormat)
ReadTextFile = ""
If Not .AtEndOfStream Then ReadTextFile = .ReadAll
.Close
End With
End Function
Sub WriteTextFile(sContent, sPath, lFormat)
' lFormat -2 - System default, -1 - Unicode, 0 - ASCII
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 2, True, lFormat)
.Write sContent
.Close
End With
End Sub
Note that it inserts the content of the line 21 before the line 14, thus preserving newline, since the content of the line 14 are newline chars. If you want just replace, then use aContent(13) = aContent(20) instead of aContent(13) = aContent(20) & vbCrLf & aContent(13).
Do Until Inp.AtEndOfStream
Count=Count + 1
Line=Inp.readline
If Count = 14 then
outp.writeline "My line 14"
Else
outp.writeline Line
End If
Loop
Is the pattern for your type of problem. Read a line, make a decision, write something.

edit my .txt doc using VBS

In VBScript, I would like to update a text file with new data and have it shown in a message box.
Below is what I have so far; what am I doing wrong?
Option Explicit
Dim oFso, Michael, John, Valery, Susane, Katterina
Dim oStream, oFolder, f, myArrayList
Const ForAppending = 8
Const ForReading = 1, ForWriting = 2
Set myArrayList = CreateObject("System.Collections.ArrayList")
myArrayList.Add "Misko, Janko, Vierka,"
'create '
Call WriteLineToFile
Function WriteLineToFile
Set oFso = CreateObject("Scripting.FileSystemObject")
Set f = oFso.CreateTextFile("D:\TestFile1.txt", 2, True)
f.WriteLine "Misko, Janko, Vierka,"
MsgBox "Subor C:\TestFile.txt bol " & "vytvoreny."
f.Close
Set f = Nothing
Set oFso = Nothing
MsgBox "Uspesne vytvoreny " & TestFile2.txt & "."
End Function
Option Explicit 'explicit declaration of all variables'
Dim oFso, f, sPath, sPath2, i, sTemp 'deklaracia'
Dim arrString
Const ForReading = 1, ForWriting = 2
sPath = "D:\TestFile1.txt" 'cesta ku datam'
sPath2 = "D:\TestFile2.txt"
arrString = Array("Marek", "Tomas") 'pole alebo polia'
ReDim arrString(2)
arrString(0) = "Misko"
arrString(1) = "Janko"
arrString(2) = "Vierka"
sTemp = "" 'empty pole pred runom'
For i = 0 To UBound(arrString) 'ide po upper bound '
If i = UBound(arrString) Then
sTemp = sTemp + arrString(i) 'odstrani ciarku na konci'
Else
sTemp = sTemp + arrString(i) + ", "
End If
Next
MsgBox sTemp
Call WriteLineToFile (sPath, sTemp) 'zavola sub routine'
ReDim Preserve arrString (4)
arrString(3) = "Zuzka"
arrString(4) = "Katka"
sTemp = ""
For i = 0 To UBound(arrString)
If i = UBound(arrString) Then
sTemp = sTemp + arrString(i)
Else
sTemp = sTemp + arrString(i) + ", "
End If
Next
MsgBox sTemp
Call WriteLineToFile (sPath2, sTemp)
Sub WriteLineToFile (sFilePath, sText)
Set oFso = CreateObject("Scripting.FileSystemObject")
Set f = oFso.CreateTextFile(sFilePath, 2, True)
'For i = 0 To UBound(arrString) - 1 'nepotrebne'
f.WriteLine sText
'Next
MsgBox "Subor " & sFilePath & " vytvoreny."
f.Close
Set f = Nothing
Set oFso = Nothing
End Sub

remove nul characters from text file using vbs

I have text files that are approximately 6MB in size. There are some lines that contain the NULL (Chr(0))character that I would like to remove.
I have two methods to do this: using Asc()=0 but this takes approximately 50s to complete, the other method uses InStr (line, Chr(0)) =0 (fast ~ 4sec)but the results remove vital info from the lines which contain the NULL characters.
First line of text file as example:
##MMCIBN.000NULL7NULL076059NULL7653NULL1375686349NULL2528NULL780608NULL10700NULL\NULL_NC_ACT.DIR\CFG_RESET.INI
First method (works but VERY slow)
function normalise (textFile )
Set fso = CreateObject("Scripting.FileSystemObject")
writeTo = fso.BuildPath(tempFolder, saveTo & ("\Output.arc"))
Set objOutFile = fso.CreateTextFile(writeTo)
Set objFile = fso.OpenTextFile(textFile,1)
Do Until objFile.AtEndOfStream
strCharacters = objFile.Read(1)
If Asc(strCharacters) = 0 Then
objOutFile.Write ""
nul = true
Else
if nul = true then
objOutFile.Write(VbLf & strCharacters)
else
objOutFile.Write(strCharacters)
end if
nul = false
End If
Loop
objOutFile.close
end function
The output looks like this:
##MMCIBN.000
7
076059
7653
1375686349
2528
780608
10700
\
_NC_ACT.DIR\CFG_RESET.INI
Second method code:
filename = WScript.Arguments(0)
Set fso = CreateObject("Scripting.FileSystemObject")
sDate = Year(Now()) & Right("0" & Month(now()), 2) & Right("00" & Day(Now()), 2)
file = fso.BuildPath(fso.GetFile(filename).ParentFolder.Path, saveTo & "Output " & sDate & ".arc")
Set objOutFile = fso.CreateTextFile(file)
Set f = fso.OpenTextFile(filename)
Do Until f.AtEndOfStream
line = f.ReadLine
If (InStr(line, Chr(0)) > 0) Then
line = Left(line, InStr(line, Chr(0)) - 1) & Right(line, InStr(line, Chr(0)) + 1)
end if
objOutFile.WriteLine line
Loop
f.Close
but then the output is:
##MMCIBN.000\CFG_RESET.INI
Can someone please guide me how to remove the NULLS quickly without losing information. I have thought to try and use the second method to scan for which line numbers need updating and then feed this to the first method to try and speed things up, but quite honestly I have no idea where to even start doing this!
Thanks in advance...
It looks like the first method is just replacing each NULL with a newline. If that's all you need, you can just do this:
Updated:
OK, sounds like you need to replace each set of NULLs with a newline. Let's try this instead:
strText = fso.OpenTextFile(textFile, 1).ReadAll()
With New RegExp
.Pattern = "\x00+"
.Global = True
strText = .Replace(strText, vbCrLf)
End With
objOutFile.Write strText
Update 2:
I think the Read/ReadAll methods of the TextStream class are having trouble dealing with the mix of text and binary data. Let's use an ADO Stream object to read the data instead.
' Read the "text" file using a Stream object...
Const adTypeText = 2
With CreateObject("ADODB.Stream")
.Type = adTypeText
.Open
.LoadFromFile textFile
.Charset = "us-ascii"
strText = .ReadText()
End With
' Now do our regex replacement...
With New RegExp
.Pattern = "\x00+"
.Global = True
strText = .Replace(strText, vbCrLf)
End With
' Now write using a standard TextStream...
With fso.CreateTextFile(file)
.Write strText
.Close
End With
I tried this method (update2) for reading a MS-Access lock file (Null characters terminated strings in 64 byte records) and the ADODB.Stream didn't want to open an already in use file. So I changed that part to :
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.GetFile(Lfile)
z = f.Size
set ts = f.OpenAsTextStream(ForReading, 0) 'TristateFalse
strLog = ts.Read(z)
ts.Close
set f = nothing
' replace 00 with spaces
With New RegExp
.Pattern = "\x00+"
.Global = True
strLog = .Replace(strLog, " ")
End With
' read MS-Access computername and username
for r = 1 to len(strLog) step 64
fnd = trim(mid(strLog,r, 32)) & ", " & trim(mid(strLog,r+32, 32)) & vbCrLf
strRpt = strRpt & fnd
next

Not getting the output for splitting the text files in vbscript

I have been using the following code to split my text file into two files.My original file only consists of 20 lines which i am trying to split into 2 files.Even when the script runs and i get the message at the end saying that the process is complete i can't see any splitted files at the output location.Please tell me what's the problem in the code;I am new to vbscript so please help me.Thanks in advance :)
Dim Counter
Const InputFile = "C:\Cs.txt"
Const OutputFile = "C:\Users\rmehta\Desktop"
Const RecordSize = 10
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile (InputFile, ForReading)
Counter = 0
FileCounter = 0
Set objOutTextFile = Nothing
Do Until objTextFile.AtEndOfStream
if Counter = 0 Or Counter = RecordSize Then
Counter = 0
FileCounter = FileCounter + 1
if Not objOutTextFile is Nothing then objOutTextFile.Close
Set objOutTextFile = objFSO.OpenTextFile( OutputFile & "_" & FileCounter & ".txt", ForWriting, True)
end if
strNextLine = objTextFile.Readline
objOutTextFile.WriteLine(strNextLine)
Counter = Counter + 1
Loop
objTextFile.Close
objOutTextFile.Close
Msgbox "Split process complete"
If you leave out all the spurious fat (the Textstream has a line counter and the first output file can be opened before the loop), you get
Option Explicit
Const cnSize = 10
Dim oFS : Set oFS = CreateObject("Scripting.FileSystemObject")
Dim sDir : sDir = "..\testdata\18308970"
Dim tsIn : Set tsIn = oFS.OpenTextFile(oFS.BuildPath(sDir, "all.txt"))
Dim nFCnt : nFCnt = 0
Dim tsOut : Set tsOut = oFS.CreateTextFile(oFS.BuildPath(sDir, nFCnt & "-part.txt"))
Do Until tsIn.AtEndOfStream
If 0 = tsIn.Line Mod cnSize Then
tsOut.Close
nFCnt = nFCnt + 1
Set tsOut = oFS.CreateTextFile(oFS.BuildPath(sDir, nFCnt & "-part.txt"))
End If
tsOut.WriteLine tsIn.ReadLine()
Loop
tsIn.Close
tsOut.Close
That this 'works' - if you have the folder, input file, and permissions - is obvious. In your code, the problem
>> Const OutputFile = "C:\Users\rmehta\Desktop"
>> FileCounter = 0
>> WScript.Echo OutputFile & "_" & FileCounter & ".txt"
>>
C:\Users\rmehta\Desktop_0.txt
is is deeply hidden.

Resources