I have pieced together a script that is working, but not 100%.
I am reading values from a file (A) and then searching in a specific position in another file (B) for a match, then writing the entire row of data to a new file (C).
The script below works great on reading the first row in the data (file A), but it won't get past the first row.
Here is a sample list of strings I am searching for from file (A).
9899008KT2018012600000444
9899008KT2018012600000445
Here is my script:
Set fso = CreateObject("Scripting.FileSystemObject")
Set inFile = fso.OpenTextFile("C:\JeffTestFolder\9899008KT2018012600.txt", 1)
Set outFile = fso.OpenTextFile("C:\TestFolder\9899008KT2018012600_Compiled.txt", 8, True)
Set listFile = fso.OpenTextFile("C:\TestFolder\ListOfIDs.txt", 1)
Do Until listFile.AtEndOfStream
fName = listFile.ReadLine
Do Until inFile.AtEndOfStream
line = inFile.ReadLine
If Mid(line, 7, 25) = fName Then outFile.WriteLine line
Loop
Loop
I am stuck on how to get it to Loop and read the next line in file (A) then go search for that value in file (B) and write it to the new file (C).
If you want to compare each line of A against each line of B you need to repeat reading B for each line of A:
Do Until listFile.AtEndOfStream
fName = listFile.ReadLine
Set inFile = fso.OpenTextFile("C:\JeffTestFolder\9899008KT2018012600.txt")
Do Until inFile.AtEndOfStream
line = inFile.ReadLine
If Mid(line, 7, 25) = fName Then outFile.WriteLine line
Loop
inFile.Close
Loop
or read the entire file into an array or dictionary and use that array/dictionary as the reference:
txt = fso.OpenTextFile("C:\JeffTestFolder\9899008KT2018012600.txt").ReadAll
arr = Split(txt, vbNewLine)
Do Until listFile.AtEndOfStream
fName = listFile.ReadLine
For Each line In arr
If Mid(line, 7, 25) = fName Then outFile.WriteLine line
Next
Loop
Set dict = CreateObject("Scripting.Dictionary")
txt = fso.OpenTextFile("C:\JeffTestFolder\9899008KT2018012600.txt").ReadAll
For Each line In Split(txt, vbNewLine)
dict(Mid(line, 7, 25)) = True
Next
Do Until listFile.AtEndOfStream
fName = listFile.ReadLine
If dict.Exists(fName) Then outFile.WriteLine line
Loop
Which one to pick depends on file size and system resources. The second and third approach provide better performance, because they avoid repeated disk I/O, but may lead to memory exhaustion when the file is large.
Related
In a text file consisting of thousands of records, each having greater than 20 lines of data, I need to count the 14th line after the start of every record if that 14th line is blank. The line is either blank or contains a date.
The start of every record is the same: "1 Start of new record"
Scenario:
1 Start of new record
2 some data
3 "
4 "
5 "
6 "
7 "
8 "
9 "
10 "
11 "
12 "
13 "
14
...
1 Start of new record
...
8 "
9 "
10 "
...
14 10/19/2019
...
In this simple scenario, the result should be 1. I have code that copies line 1 of every record into a second file.
The result obviously being:
1 Start of new record
1 Start of new record
...
Here is the code I have:
Const ForReading = 1
Dim words(1)
Dim msg
words(0) = "1 Start of New Record"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set inFile = objFSO.OpenTextFile("c:\Temp\altest.txt", ForReading)
Set outFile = objFSO.OpenTextFile("c:\Temp\altest_output.txt", 8, True)
Do Until inFile.AtEndOfStream
strSearchString = inFile.ReadLine
For i = 0 To UBound(words)-1
If InStr(strSearchString,words(i)) Then
msg = msg&strSearchString&vbcrlf
End If
next
Loop
inFile.Close
outfile.WriteLine msg
WScript.Echo "Done!"
This seems like a good start, but again, I need to count the 14th line after the start of every record if that 14th line is blank.
Any help is greatly appreciated.
-Alel
Hardly elegant but something like this should get you on your way. This doesn't use SkipLine, it just marks the next line of interest:
Option Explicit 'force explicit variable declaration, this is just good practice
Const ForReading = 1
Dim strContent
Dim Offset : Offset = 14 'define the 14th 'line'
Dim StartLine
Dim NewRecordMarker : NewRecordMarker = "1 Start of new record" 'just use a string to match
Dim objFSO, inFile, outFile
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set inFile = objFSO.OpenTextFile("e:\Temp\altest.txt", ForReading)
Set outFile = objFSO.OpenTextFile("e:\Temp\altest_output.txt", 8, True)
'notice we're only reading forward
'that means we can set the next LineOfInterest without having to worry about
'exceeding AtEndOfStream like we would if we'd use SkipLine
'this is just simpler.
'this obviously falls apart when the line of interest is NOT the 14th line
Do Until inFile.AtEndOfStream
Dim LineOfInterest
strContent = inFile.ReadLine 'inFile.Line will at 2 at this point because we just read it
If strContent = NewRecordMarker Then 'found a new record, we want to look 14 lines from here
LineOfInterest = inFile.line - 1 + Offset ' -1 or we'll overshoot our target
End If
If inFile.Line = LineOfInterest Then 'this is the line we want to inspect
outFile.WriteLine strContent 'just write out entire value, no checking for date here
End If
Loop
inFile.Close
outFile.Close
WScript.Echo "Done!"
I have a .txt file which I wish to edit in VBS. The data is like the following:
Time, Column 1, Column 2
23/08/2017 8:30:00 AM, Data, Data
23/08/2017 8:35:00 AM, Data, Data
23/08/2017 8:40:00 AM, Data, Data
23/08/2017 8:45:00 AM, Data, Data
What I want is another 'column' called batch added at the end of the first line and then, the first value of time (23/08/2017 8:30:00 AM) to make up the data for this column so that the end result is something like the following:
Time, Column 1, Column 2, Batch
23/08/2017 8:30:00 AM, Data, Data, 23/08/2017 8:30:00 AM
23/08/2017 8:35:00 AM, Data, Data, 23/08/2017 8:30:00 AM
23/08/2017 8:40:00 AM, Data, Data, 23/08/2017 8:30:00 AM
23/08/2017 8:45:00 AM, Data, Data, 23/08/2017 8:30:00 AM
Note a comma separator exists between each column.
You can do something like this:
Start reading the data from the File line by line and store it in a temporary variable along with the data you want to append at the end of each line.
For the first line, data to be appended is ", Batch" and for the remaining lines data to be appended is the "Time Value" from the 2nd Line.
Open the file in write mode and write the data stored in that temporary variable
Code:
strPath = "C:\Users\gr.singh\Desktop\Desktop\Gurman\2017\as.txt" 'Replace this path with your file path
Set fso = CreateObject("scripting.filesystemobject")
Set rfile = fso.OpenTextFile(strPath,1) 'File opened in Read-only mode
While Not rfile.AtEndOfStream
temp=rfile.ReadLine()
If rfile.Line=2 Then 'The first line has been read by using the readline method due to which rfile.line gets set to 2. Hence, I have used 2 here for the 1st line. Similarly, I have used 3 fro the 2nd line in the ElseIf Condition
dataToAppend = "Batch"
ElseIf rfile.Line=3 Then
dataToAppend = Split(temp,",")(0)
End If
fulldata = fulldata & temp&", "&dataToAppend&"||"
Wend
rfile.Close
fulldata = Left(fulldata,Len(fulldata)-2)
Set wfile = fso.OpenTextFile(strPath,2) 'File opened in write mode
tempArr = Split(fulldata,"||")
For i=0 To UBound(tempArr)
wfile.WriteLine tempArr(i)
Next
wfile.Close
Set fso= Nothing
OUTPUT:
If your file is reasonably small you can read it as a whole and process it like this:
filename = "C:\path\to\your.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
txt = fso.OpenTextFile(filename).ReadAll
data = Split(txt, vbNewLine)
If UBound(data) >= 0 Then data(0) = data(0) & ",batch"
If UBound(data) >= 1 Then
batchval = Left(data(1), InStr(data(1), ",")-1)
data(1) = data(1) & "," & batchval
End If
For i = 2 To UBound(data)
data(i) = data(i) & "," & batchval
Next
fso.OpenTextFile(filename, 2).Write Join(data, vbNewLine)
For large files this approach is not recommended, though, because it may lead to memory exhaustion, causing your computer to come grinding to a halt. If your file is large you're better off processing the file line by line, writing the output to a temporary file, and replacing the original once you're finished.
filename = "C:\path\to\your.txt"
tmpfilename = filename & ".tmp"
Set fso = CreateObject("Scripting.FileSystemObject")
inFile = fso.OpenTextFile(filename)
outFile = fso.OpenTextFile(tmpfilename, 2, True)
If Not inFile.AtEndOfStream Then outFile.WriteLine inFile.ReadLine & ",batch"
If Not inFile.AtEndOfStream Then
line = inFile.ReadLine
batchval = Left(line, InStr(line, ",")-1)
outFile.WriteLine line & "," & batchval
End If
Do Until inFile.AtEndOfStream
outFile.WriteLine inFile.ReadLine & "," & batchval
Loop
inFile.Close
outFile.Close
fso.DeleteFile filename, True
fso.MoveFile tmpfilename, filename
I got this code which deletes 10 lines starting from the top.
Is it possible to do the same but starting the delete from the bottom to the top of the txt file?
So if I have 30 lines, I want the last 10 or 20 lines to be deleted.
Const FOR_READING = 1
Const FOR_WRITING = 2
strFileName = "C:\scripts\delete.txt"
iNumberOfLinesToDelete = 10
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objTS = objFS.OpenTextFile(strFileName, FOR_READING)
strContents = objTS.ReadAll
objTS.Close
arrLines = Split(strContents, vbNewLine)
Set objTS = objFS.OpenTextFile(strFileName, FOR_WRITING)
For i=0 To UBound(arrLines)
If i > (iNumberOfLinesToDelete - 1) Then
objTS.WriteLine arrLines(i)
End If
Next
If you read the entire file into an array of lines you'd use more or less the same approach for removing lines from beginning or end.
To remove lines from the beginning you start at an offset after the lines that you want removed:
filename = "C:\path\to\your.txt"
numLinesToRemove = 10
Set fso = CreateObject("Scripting.FileSystemObject")
txt = Split(fso.OpenTextFile(filename).ReadAll, vbNewLine)
Set f = fso.OpenTextFile(filename, 2)
For i = numLinesToRemove To UBound(txt)
f.WriteLine txt(i)
Next
f.Close
To remove lines from the end of the file you stop before the lines that you want removed:
filename = "C:\path\to\your.txt"
numLinesToRemove = 10
Set fso = CreateObject("Scripting.FileSystemObject")
txt = Split(fso.OpenTextFile(filename).ReadAll, vbNewLine)
Set f = fso.OpenTextFile(filename, 2)
For i = 0 To UBound(txt) - numLinesToRemove
f.WriteLine txt(i)
Next
f.Close
This approach only works for small files, though. If you need to process large files you usually can't read the entire file into memory. If you did your computer would start swapping data from memory to disk, causing the system to slow down to a crawl. To avoid this you normally read the file line by line in a loop and write to a temporary file, then replace the original file with the temp file after processing is complete.
Removing lines from the beginning of a file is still fairly trivial, because TextStream objects have a Line property that holds the current line number (i.e. the number of the line that the next ReadLine call would read).
Set f = fso.OpenTextFile(filename)
Set tmp = fso.OpenTextFile(filename & ".tmp", 2, True)
Do Until f.AtEndOfStream
If f.Line <= numLinesToRemove Then
f.SkipLine
Else
tmp.WriteLine f.ReadLine
End If
Loop
f.Close
tmp.Close
However, you can't do that for removing lines from the end of the file, because you don't know the number of lines beforhand. One way to deal with this is to create a ring buffer the size of the number of lines you want to remove, fill it as you read lines from the input file, and write lines to the output file when they are removed from the buffer. That way the last numLinesToRemove lines are still in the buffer (not written to the output file) when the loop terminates.
ReDim buf(numLinesToRemove) 'ring buffer
i = -1 'ring buffer pointer
Set f = fso.OpenTextFile(filename)
Set tmp = fso.OpenTextFile(filename & ".tmp", 2, True)
Do Until f.AtEndOfStream
i = (i + 1) Mod numLinesToRemove 'advance ring buffer pointer
'if current buffer slot is filled write it to the output file ...
If Not IsEmpty(buf(i)) Then tmp.WriteLine buf(i)
'... then put current line from input file into current buffer slot
buf(i) = f.ReadLine
Next
f.Close
tmp.Close
In both cases you'd replace the original file after processing is complete, e.g. like this:
fso.DeleteFile filename
fso.MoveFile filename & ".tmp", filename
just loop backwards in your for statement
For i=UBound(arrLines) To (UBound(arrLines) -10) step -1
Next
I understand that the FSO does not know to read the lines from the last in a file.
My scenario here is to validate the last but 1 line and get the result out of it.
Assume, if i need to get the result as PASS or FAIL in the last but 1 line. Since i go through from the first line, the scenario of me getting the correct result is limited because there is a probability of PASS or FAIL appearing in the file earlier.
My last 2 lines in the file is
Failed
Done!!!!
OR
Passed
Done!!!!
to get the actual i am using a NESTED IF validation to get the result. Below is the snippet of the same.
str1 = "Passed"
str2 = "Failed"
str3="Done!!!!"
Do Until objFile.AtEndOfStream
str=objFile.ReadLine
if StrComp(str, str1) = 0 Then
str=objFile.ReadLine
if StrComp(str,str3) = 0 Then
result="PASS"
End if
elseif StrComp(str, str2) = 0 Then
str = objFile.ReadLine
if StrComp(str,str3) = 0 Then
result="FAIL"
End if
End if
Loop
This affects the performance. Is there any alternative to get this implementation in a better manner?
Here is a function which takes a file name and returns the second to last line read:
Function PenultimateLine(fname)
Dim fso, ts, line1, line2
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(fname)
Do Until ts.AtEndOfStream
line1 = line2
line2 = ts.ReadLine
Loop
ts.Close
PenultimateLine = line1
End Function
You can use this function to extract the line and then test it against "PASS" or "FAIL" (which, by the way, can be done simply with = rather than StrCmp)
A = Split(objfile.readall, vbcrlf)
B = A(ubound(A)-2)
This uses memory and is unsuitable on very large files.
I have a file in which I am able to find the keyword using vbscript but further I need to keep copying next 3-4 lines down it, until I find another occurrence of similar pattern of keyword.
I have written something like this - ( I am newbee for assume I am dumb )
Set fso = CreateObject("Scripting.FileSystemObject")
Set inFile = fso.OpenTextFile("FileName", 1)
Set outFile = fso.OpenTextFile("FileName", 8, True)
outFile.WriteLine("This is some sample data.")
strAnswer = InputBox("Please enter a value:", _
"Enter Value")
Do until inFile.AtEndOfStream
line = inFile.ReadLine
If InStr(line, strAnswer) Then
outFile.WriteLine line ' Copy the line and write to output file
serNum = Left(line, 7)
'If Not ((line = inFile.ReadLine())
'take first 3 char and find the next occurance of it
'copy all lines until that line
WScript.Echo "Found"
End If
Loop
outfile.Close
Set fSO = Nothing
Any suggestion is appreciable.
You could use a sub-loop to continue writing until the serial number is found again.
Do until inFile.AtEndOfStream
line = inFile.ReadLine
If InStr(line, strAnswer) Then
outFile.WriteLine line ' Copy the line and write to output file
serNum = Left(line, 7)
' Continue writing until EOF or serial number is found...
Do While Not inFile.AtEndOfStream
line = inFile.ReadLine
If InStr(line, serNum) = 0 Then outFile.WriteLine line
Loop
End If
Loop
Use the state of the variable serNum to decide what you need to do with the current line:
Do until inFile.AtEndOfStream
line = inFile.ReadLine
If Not IsEmpty(serNum) And InStr(line, serNum) > 0 Then WScript.Quit
If IsEmpty(serNum) And InStr(line, strAnswer) > 0 Then serNum = Left(line, 7)
If Not IsEmpty(serNum) Then outFile.WriteLine line
Loop
The first condition checks if you have found the second match and then quits.
The second condition checks if you have found the first match and then initializes serNum.
The third condition causes all lines from the first match to the line before the second match to be written to the output file.